Test a function that contains a setTimeout()

I have a method that is eerily similar to the OP's setup so thought I would add my test which I think is a lot simpler.

** Method **

public close() {
    this.animate = "inactive"
    setTimeout(() => {
       this.show = false
    }, 250)
}

** Jasmine Test **

it('should set show to "false" after 250ms when close is fired', (done) => {
      component.close();
      setTimeout(() => {
        expect(component.close).toBeFalse();
        done();
      }, 251);
    });

Note the use of 'done' to let jasmine know the test is finished and also adding 1 ms to the setTimeout to fire after my method's setTimeout.


You could do one of two things:

1: Actually wait in the test 250+1 ms in a setTimeout(), then check if the element actually disappeared.

2: use fakeAsync() and tick() to simulate time in the test - a tick() will resolve the setTimeout in the original close(), and the check could happen right after in a fixture.whenStable().then(...).

For example:

it("tests the exit button click", fakeAsync(() => {
  comp.close()
  tick(500)
  fixture.detectChanges()

  fixture.whenStable().then(() => {
    const popUpWindow = fixture.debugElement.query(By.css("#popup-window"))
    expect(popUpWindow).toBe(null)
  })
}))

I suggest using the 2nd one, as it is much more faster than actually waiting for the original method. If you still use the 1st, try lowering the timeout time before the test to make the it run faster.

SEVICES

For services you do not need to call detectChanges after tick and do not need to wrap the expect statements within whenStable. you can do your logic right after tick.

  it('should reresh token after interval', fakeAsync(() => {
    // given
    const service: any = TestBed.get(CognitoService);
    const spy = spyOn(service, 'refreshToken').and.callThrough();
    ....
    // when
    service.scheduleTokenRefresh();
    tick(TOKEN_REFRESH_INTERVAL);
    // then
    expect(spy).toHaveBeenCalled();
  }));

Just tried this in my project and works:

jasmine.clock().tick(10);

In my component the method is:

hideToast() {
    setTimeout( () => {
      this.showToast = false;
    }, 5000);
  }

Test for that (explanation in comments):

it('should hide toast', () => {
  component.showToast = true; // This value should change after timeout
  jasmine.clock().install();  // First install the clock
  component.hideToast();      // Call the component method that turns the showToast value as false
  jasmine.clock().tick(5000); // waits till 5000 milliseconds
  expect(component.showToast).toBeFalsy();  // Then executes this
  jasmine.clock().uninstall(); // uninstall clock when done
});

Hope this helps!!