2

In my ngOnInit I check a route, do some stuff, and then setup an interval

ngOnInit(): void {
    this.routeSubscription = this.route.paramMap.subscribe(x => {
         ...
         // Reload the data every 60 seconds
         interval(60_000)
             .pipe(takeUntil(this.cancelInterval$))
             .subscribe(() => this.rows$ = this.orderService.active())
    })
}

When I try to do my unit testing, jasmine aborts because the async method didn't return fast enough. I'm doing this (as a sub-describe of the main one, which does all the normal TestBed setups):

describe('when showing active orders', () => {
    beforeEach(() => {
        activatedRoute.setParamMap({})
        fixture.detectChanges()
    })

    it('...', async () => {
        await fixture.whenStable()
        ...
    })

It seems to be stuck because of the interval. I thought I could just put a call to discardPeriodicTasks() at the end of the beforeEach method, but that doesn't work.

So I'm looking for either the right way to handle this in testing, or a way in the production code to only make the interval call if it's not being run via testing.

Angular CLI: 9.0.7
Node: 14.4.0
Angular: 9.1.6

Gargoyle
  • 9,590
  • 16
  • 80
  • 145

2 Answers2

1

Instead of calling RXJS interval() directly, create a simple intermediate service like this one:

import { Injectable } from '@angular/core';
import { interval } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class TimerService {

  interval(ms: number) {
      return interval(ms);
  }

}

Inject and use it within your component: timer.interval(60_000)

Now it is trivial to mock the necessary behavior of the interval() within your unit-tests, e.g.:

providers: [{ provide: TimerService, useValue: { interval: () => of(1) } }],
Vilmantas Baranauskas
  • 6,596
  • 3
  • 38
  • 50
0

You can use fakeAsync and tick to simulate the async behaviour

it('...', fakeAsync () => { tick(60000); doTestStuff(); })

phhbr
  • 2,699
  • 1
  • 14
  • 25
  • This won't work, in my understanding. The problem with interval() is that it continuously schedules the next timeout and unit-test framework waits forever till all open timeouts finish. – Vilmantas Baranauskas Sep 18 '20 at 13:52
  • well, I'd expect a ngOnDestroy on the component which will close the intervall (this.cancelInterval$) on the component & be called after successful assertion.. – phhbr Sep 18 '20 at 14:01