Explain "jest.advanceTimersToNextTimer "

Viewed 221

I'm learning testing with jest and I came up with this method: jest.advanceTimersToNextTimer. Jest's manual says:

Advances all timers by the needed milliseconds so that only the next timeouts/intervals will run. Optionally, you can provide steps, so it will run steps amount of next timeouts/intervals.

But it's not clear to me how it works. Can someone please explain me what it does, and give an example of usage of this? thank you.

1 Answers

When use jest.advanceTimersToNextTimer(steps), you don't need to provide the milliseconds manually anymore to run the macro-tasks have been queued via setTimeout() or setInterval(). Like the doc said:

Advances all timers by the needed milliseconds so that only the next timeouts/intervals will run.

Besides, you can provide a steps to jest.advanceTimersToNextTimer() method so that it will run steps amount of next timeouts/intervals. See "should pass with steps" test case. The default steps is 1, see here

When use jest.advanceTimersByTime(), if there are a lot of macro-tasks queued via setTimeout() or setInterval(), if we want to advance all timers, we have to calculate the msToRun milliseconds by ourself. This is maybe very cumbersome. See "should pass advance all timers" test case.

Besides, sometimes you may want to check the status before the timer execution. In this case, you have to use jest.advanceTimersByTime(), see this real world example, the timer is 200 millisecond,but they check the state of the fast forward in 199 milliseconds.

jest.advanceTimersToNextTimer() use click.next() method of the @sinonjs/faker-timers package underly.

Advances the clock to the the moment of the first scheduled timer, firing it.

jest.advanceTimersByTime() just use clock.tick(time) underly.

See the below example, three test cases do the same thing in different ways.

describe('71667406', () => {
  beforeAll(() => {
    jest.useFakeTimers();
  });
  afterAll(() => {
    jest.useRealTimers();
  });
  describe('advanceTimersToNextTimer', () => {
    test('should pass', () => {
      const runOrder: Array<string> = [];
      const mock1 = jest.fn(() => runOrder.push('mock1'));
      const mock2 = jest.fn(() => runOrder.push('mock2'));
      const mock3 = jest.fn(() => runOrder.push('mock3'));
      const mock4 = jest.fn(() => runOrder.push('mock4'));

      setTimeout(mock1, 100);
      setTimeout(mock2, 0);
      setTimeout(mock3, 0);
      setInterval(() => {
        mock4();
      }, 200);

      jest.advanceTimersToNextTimer();
      // Move forward to t=0
      expect(runOrder).toEqual(['mock2', 'mock3']);

      jest.advanceTimersToNextTimer();
      // Move forward to t=100
      expect(runOrder).toEqual(['mock2', 'mock3', 'mock1']);

      jest.advanceTimersToNextTimer();
      // Move forward to t=200
      expect(runOrder).toEqual(['mock2', 'mock3', 'mock1', 'mock4']);

      jest.advanceTimersToNextTimer();
      // Move forward to t=400
      expect(runOrder).toEqual(['mock2', 'mock3', 'mock1', 'mock4', 'mock4']);
    });

    test('should pass with steps', () => {
      const runOrder: Array<string> = [];
      const mock1 = jest.fn(() => runOrder.push('mock1'));
      const mock2 = jest.fn(() => runOrder.push('mock2'));
      const mock3 = jest.fn(() => runOrder.push('mock3'));
      const mock4 = jest.fn(() => runOrder.push('mock4'));

      setTimeout(mock1, 100);
      setTimeout(mock2, 0);
      setTimeout(mock3, 0);
      setInterval(() => {
        mock4();
      }, 200);

      jest.advanceTimersToNextTimer(4);
      expect(runOrder).toEqual(['mock2', 'mock3', 'mock1', 'mock4', 'mock4']);
    });
  });

  describe('advanceTimersByTime', () => {
    test('should pass', () => {
      const runOrder: Array<string> = [];
      const mock1 = jest.fn(() => runOrder.push('mock1'));
      const mock2 = jest.fn(() => runOrder.push('mock2'));
      const mock3 = jest.fn(() => runOrder.push('mock3'));
      const mock4 = jest.fn(() => runOrder.push('mock4'));

      setTimeout(mock1, 100);
      setTimeout(mock2, 0);
      setTimeout(mock3, 0);
      setInterval(() => {
        mock4();
      }, 200);

      jest.advanceTimersByTime(0);
      // Move forward to t=0
      expect(runOrder).toEqual(['mock2', 'mock3']);

      jest.advanceTimersByTime(100);
      // Move forward to t=100
      expect(runOrder).toEqual(['mock2', 'mock3', 'mock1']);

      jest.advanceTimersByTime(200);
      // Move forward to t=200
      expect(runOrder).toEqual(['mock2', 'mock3', 'mock1', 'mock4']);

      jest.advanceTimersByTime(200);
      // Move forward to t=400
      expect(runOrder).toEqual(['mock2', 'mock3', 'mock1', 'mock4', 'mock4']);
    });

    test('should pass advance all timers', () => {
      const runOrder: Array<string> = [];
      const mock1 = jest.fn(() => runOrder.push('mock1'));
      const mock2 = jest.fn(() => runOrder.push('mock2'));
      const mock3 = jest.fn(() => runOrder.push('mock3'));
      const mock4 = jest.fn(() => runOrder.push('mock4'));

      setTimeout(mock1, 100);
      setTimeout(mock2, 0);
      setTimeout(mock3, 0);
      setInterval(() => {
        mock4();
      }, 200);

      // We need to calculate it, this is very cumbersome
      jest.advanceTimersByTime(0 + 100 + 200 + 200);
      expect(runOrder).toEqual(['mock2', 'mock3', 'mock1', 'mock4', 'mock4']);
    });
  });
});

Test result:

 PASS  stackoverflow/71667406/index.test.ts
  71667406
    advanceTimersToNextTimer
      ✓ should pass (4 ms)
      ✓ should pass with steps
    advanceTimersByTime
      ✓ should pass (1 ms)

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        1.242 s

I also hope that Jest official can give more examples to explain the use scenario of the API.

Related