Testing an observable interval in ngOnInit

Viewed 230

I've been trying to get this test running for over a day now, and I am out of options.

I have tried using variants of fakeAsync, and injecting a testScheduler, but the tick() function seems to have no effect, and, as I will describe, injecting a testScheduler works too well.

The function I wish to test is this:

ngOnInit() {
  ...
  interval(environment.verificationCheckPollingInterval).pipe(takeUntil(this.destroy)).subscribe(_ =>
    this.store.dispatch(fromAccount.checkEmailVerificationStatus()));
}

I do realise that polling for email verification isn't ideal practice, but that is a story for another day.

My jasmine testing function looks like this:

describe('After the verification interval has elapsed', () => {
  it('should dispatch a check verification email action after the verification interval has elapsed', fakeAsync(() => {
    tick(environment. environment.verificationCheckPollingInterval);
    fixture.detectChanges();
    testScheduler.run(({ expectObservable, flush, cold }) => {
      expectObservable(store.scannedActions$.pipe(
        ofType(fromAccount.ActionTypes.checkEmailVerificationStatus),
        map((_) => true))).toBe('a', { a: true });
    });
  }));
});

The tick function seems to do nothing at all.

Another approach is to inject a scheduler into the component, and to change the subscription to

interval(environment.verificationCheckPollingInterval, this.scheduler).pipe(takeUntil(this.destroy)).subscribe(_ =>
    this.store.dispatch(fromAccount.checkEmailVerificationStatus()));

and then in my test setup, include

{ provide: Scheduler, useValue: testScheduler }

But this works too well (besides the fact that I'll be modifying production code to support tests, which is far from ideal). The reason it works too well is that if I put a breakpoint on the subscription handler, it gets called repeatedly without any delays, and at each step, the testScheduler.frame value has advanced by 1000 frames (my polling interval is 1 second). This hangs my browser and makes my laptop heat up somewhat alarmingly.

Am I missing something? Is there a way to test interval without setting my laptop on fire?

UPDATE

I have had one very sensible suggestion, which unfortunately failed.

Given this code:

describe('After the verification interval has elapsed', () => {
  it('should dispatch a check verification email action after the verification interval has elapsed', (done) => {
    store.scannedActions$.pipe(
      ofType(fromAccount.ActionTypes.checkEmailVerificationStatus),
      take(1)).subscribe(x => {
        console.log('action dispatched', x);
        done();
    });
  });
});

the console.log call gets invoked, but the test fails with the message

Error: Timeout - Async callback was not invoked within 5000ms (set by jasmine.DEFAULT_TIMEOUT_INTERVAL)

1 Answers

You could simply subscribe to the store and call done when you are done with the tests.

For more info, you can read it here

describe('After the verification interval has elapsed', () => {
  it('should dispatch a check verification email action after the verification interval has elapsed', (done) => {
      store.scannedActions$.pipe(
        ofType(fromAccount.ActionTypes.checkEmailVerificationStatus))
        .subscribe(_ => {
            // run your tests here
            done();
        });
  });
});
Related