计时器模拟

info" class="reference-link">计时器模拟 - 图3info

Also see documentation.

In the following example we enable fake timers by calling jest.useFakeTimers(). This is replacing the original implementation of setTimeout() and other timer functions. 计时器可以恢复他们默认的行为通过jest.useRealTimers().

timerGame.js

__tests__/timerGame-test.js

  1. jest.useFakeTimers();
  2. jest.spyOn(global, 'setTimeout');
  3. test('waits 1 second before ending the game', () => {
  4. const timerGame = require('../timerGame');
  5. timerGame();
  6. expect(setTimeout).toHaveBeenCalledTimes(1);
  7. expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);
  8. });
  1. jest.useFakeTimers();
  2. test('calls the callback after 1 second', () => {
  3. const timerGame = require('../timerGame');
  4. const callback = jest.fn();
  5. timerGame(callback);
  6. // At this point in time, the callback should not have been called yet
  7. expect(callback).not.toBeCalled();
  8. // Fast-forward until all timers have been executed
  9. jest.runAllTimers();
  10. expect(callback).toBeCalled();
  11. expect(callback).toHaveBeenCalledTimes(1);

There are also scenarios where you might have a recursive timer – that is a timer that sets a new timer in its own callback. For these, running all the timers would be an endless loop, throwing the following error: “Aborting after running 100000 timers, assuming an infinite loop!”

If that is your case, using jest.runOnlyPendingTimers() will solve the problem:

infiniteTimerGame.js

__tests__/infiniteTimerGame-test.js

  1. jest.useFakeTimers();
  2. jest.spyOn(global, 'setTimeout');
  3. describe('infiniteTimerGame', () => {
  4. test('schedules a 10-second timer after 1 second', () => {
  5. const infiniteTimerGame = require('../infiniteTimerGame');
  6. const callback = jest.fn();
  7. infiniteTimerGame(callback);
  8. // At this point in time, there should have been a single call to
  9. // setTimeout to schedule the end of the game in 1 second.
  10. expect(setTimeout).toHaveBeenCalledTimes(1);
  11. expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);
  12. // Fast forward and exhaust only currently pending timers
  13. // (but not any new timers that get created during that process)
  14. jest.runOnlyPendingTimers();
  15. // At this point, our 1-second timer should have fired its callback
  16. expect(callback).toBeCalled();
  17. // And it should have created a new timer to start the game over in
  18. // 10 seconds
  19. expect(setTimeout).toHaveBeenCalledTimes(2);
  20. expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 10000);
  21. });
note" class="reference-link">note
  1. jest.useFakeTimers({timerLimit: 100});

另一种可选方式是使用 jeste. advancertimersbytime (msToRun)。 通过调用msToRun这个 API时, 所有计时器都将以毫秒增长。 When this API is called, all timers are advanced by milliseconds. 所有通过setTimeout() 或setInterval() 而处于任务队列中等待中的“宏任务”和一切其他应该在本时间片中被执行的东西都应该被执行。 Additionally, if those macro-tasks schedule new macro-tasks that would be executed within the same time frame, those will be executed until there are no more macro-tasks remaining in the queue that should be run within msToRun milliseconds.

timerGame.js

__tests__/timerGame-test.js

  1. jest.useFakeTimers();
  2. it('calls the callback after 1 second via advanceTimersByTime', () => {
  3. const timerGame = require('../timerGame');
  4. const callback = jest.fn();
  5. timerGame(callback);
  6. // At this point in time, the callback should not have been called yet
  7. expect(callback).not.toBeCalled();
  8. // Fast-forward until all timers have been executed
  9. jest.advanceTimersByTime(1000);
  10. // Now our callback should have been called!
  11. expect(callback).toBeCalled();
  12. expect(callback).toHaveBeenCalledTimes(1);
  13. });

最后,在某些测试中你可能需要清除所有等待状态下的定时器,为此,可以使用 jest.clearAllTimers()。 For this, we have jest.clearAllTimers().

  1. /**
  2. * @jest-environment jsdom
  3. */
  4. const mockPerformanceMark = jest.fn();
  5. window.performance.mark = mockPerformanceMark;
  6. test('allows mocking `performance.mark()`', () => {
  7. jest.useFakeTimers({doNotFake: ['performance']});
  8. expect(window.performance.mark).toBe(mockPerformanceMark);