Angular - Long Polling with Observables - Unit Testing

Viewed 190

I'm quite new to unit testing observables, so far I found out that there are two ways of testing them either by using the subscribe and assert pattern or by using marble testing.

I do now have a task at hand where I need some sort of long polling implementation. The implementation is working fine, but writing the according unit test is a pain.

Here is the code I have so far:

long-polling.service.ts

// ...
/**
* Starts polling a long polling endpoint. Pass in any kind of observable stream
* to the method and eg. from the httpClient.get(). You can also configure a maximum
* number of retries in case the server might fail and the retry interval in ms.
* @param observableToPoll$
* @param maxRetrys
* @param retryIntervalOnError
*/
startPolling<T>(
  observableToPoll$: Observable<T>,
  maxRetrys: number = Infinity,
  retryIntervalOnError = 5000
): Observable<T> {
  if (this.poll$) {
    return this.poll$;
  }

  this.poll$ = observableToPoll$.pipe(
    longPollUntil(this.stopPolling$, maxRetrys, retryIntervalOnError)
  );

  return this.poll$;
}
//...

util.ts

export function longPollUntil<T>(
  isPollingActive: IsActiveCallback | Observable<any>,
  maxRetrys = Infinity,
  retryInterval = 5000
): MonoTypeOperatorFunction<T> {
  return (source$) => {
    const poll$ = source$.pipe(
      repeat(),
      retryWhen((errors) =>
        errors.pipe(
          concatMap((e, i) =>
            iif(
              () => i >= maxRetrys,
              throwError(e),
              of(e).pipe(delay(retryInterval))
            )
          )
        )
      ),
      share()
    );

    if (isObservable(isPollingActive)) {
      return poll$.pipe(takeUntil(isPollingActive));
    }

    return poll$.pipe(takeWhile(isPollingActive as (res: T) => boolean, true));
  };
}

long-polling.service.spec.ts

// ...
it('should start polling and repeat after stream ends', fakeAsync((done) => {
  const httpClient = TestBed.inject(HttpClient);


  const polling = service.startPolling(
    httpClient.get('/sometest/test.json').pipe(
      // observeOn(scheduler),
      tap(() => {
        console.log('request fired');
      })
    )
  );
  let callCount = 0;
  polling.subscribe((data: any) => {
    if (callCount === 0) {
      expect(data.someMessage).toBe('Test message');
    } else {
      expect(data.someMessage).toBe('Another message');
    }

    callCount++;

    if (callCount > 2) {
      done();
    }
  }, done.fail);
  const req = httpMock.match('/sometest/test.json');
  expect(req[0].request.method).toBe('GET');

  scheduler.flush();

  req[0].flush({
    someMessage: 'Test message',
  });
  scheduler.flush();

  //tick(70000);
  req[1].flush({
    someMessage: 'Another message',
  });
}));
// ...

As you can see I tried several approaches already. But the request is only fired once and does not repeat like it should do.

Does anyone have a clue how to test this?

0 Answers
Related