'Angular testing async function generate error - 1 periodic timer(s) still in the queue

I'm trying to test that two async functions. The problem is that one of the functions is called every 10 seconds automatically. I tried to use tick() or flush() but I still get the same error: 1 periodic timer(s) still in the queue. How can I resolve it? My code:

 ngOnInit(): void {
    const dialogRef = this.openProgressDialog();
    this.getSubscription = this.getAll();
    dialogRef.close();

    this.postSubscription = interval(10000).subscribe(() => {
      this.sendAll();
      this.dataSource.data = this.jobs;
      this.table.renderRows();
    });
  }

The test:

test("test", fakeAsync(() => {
    const getSpy = spyOn(otdRepositoryMock, "getAll").and.returnValue(of(jobs));
    const getSubscribeSpy = spyOn(otdRepositoryMock.getAll(), "subscribe");
    const postSpy = spyOn(otdRepositoryMock, "sendAll").and.returnValue(of(jobs));
    const postSubscribeSpy = spyOn(otdRepositoryMock.sendAll([2]), "subscribe");
    component.ngOnInit();
    //tick();
    //flush();
    expect(getSpy).toHaveBeenCalled();
    expect(getSubscribeSpy).toHaveBeenCalled();
    expect(postSpy).toHaveBeenCalled();
    expect(postSubscribeSpy).toHaveBeenCalled();
  }));


Solution 1:[1]

In short, you DON'T need flush, you DO need tick() and you should add the following to the bottom of your test, following your expectations:

      discardPeriodicTasks(); 

This will clear out any remaining timers you have before executing the next test.

So in your example (edited for brevity):

test("test", fakeAsync(() => {

   const getSpy = ...

   component.ngOnInit();
   tick(10000);

   expect(getSpy).toHaveBeenCalled() . . .

   discardPeriodicTasks(); 
 }));

Hope this helps,

Solution 2:[2]

If this.postSubscription is a public variable, you can unsubscribe in your unit test.

test("test", fakeAsync(() => {
    const getSpy = spyOn(otdRepositoryMock, "getAll").and.returnValue(of(jobs));
    const getSubscribeSpy = spyOn(otdRepositoryMock.getAll(), "subscribe");
    const postSpy = spyOn(otdRepositoryMock, "sendAll").and.returnValue(of(jobs));
    const postSubscribeSpy = spyOn(otdRepositoryMock.sendAll([2]), "subscribe");
    component.ngOnInit();
    //tick();
    //flush();
    expect(getSpy).toHaveBeenCalled();
    expect(getSubscribeSpy).toHaveBeenCalled();
    expect(postSpy).toHaveBeenCalled();
    expect(postSubscribeSpy).toHaveBeenCalled();
    // unsubscribe
    component.postSubscription.unsubscribe();
  }));

If it is private, you can unsubscribe in the ngOnDestroy and call it in the unit test.

ngOnDestroy() {
  this.postSubscription.unsubscribe();
}
...
test("test", fakeAsync(() => {
    const getSpy = spyOn(otdRepositoryMock, "getAll").and.returnValue(of(jobs));
    const getSubscribeSpy = spyOn(otdRepositoryMock.getAll(), "subscribe");
    const postSpy = spyOn(otdRepositoryMock, "sendAll").and.returnValue(of(jobs));
    const postSubscribeSpy = spyOn(otdRepositoryMock.sendAll([2]), "subscribe");
    component.ngOnInit();
    //tick();
    //flush();
    expect(getSpy).toHaveBeenCalled();
    expect(getSubscribeSpy).toHaveBeenCalled();
    expect(postSpy).toHaveBeenCalled();
    expect(postSubscribeSpy).toHaveBeenCalled();
    // call ngOnDestroy to unsubscribe
    component.ngOnDestroy();
  }));

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 djmarquette
Solution 2 AliF50