Timer Mocks

    info

    Also see Fake Timers API 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. Timers can be restored to their normal behavior with 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. // Now our callback should have been called!
    11. });

    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. });

    Timer Mocks - 图2note

    1. jest.useFakeTimers({timerLimit: 100});

    Another possibility is use . When this API is called, all timers are advanced by msToRun milliseconds. All pending “macro-tasks” that have been queued via setTimeout() or setInterval(), and would be executed during this time frame, will be executed. 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. });

    Lastly, it may occasionally be useful in some tests to be able to clear all of the pending timers. For this, we have jest.clearAllTimers().

    Sometimes your code may require to avoid overwriting the original implementation of one or another API. If that is the case, you can use doNotFake option. For example, here is how you could provide a custom mock function for performance.mark() in jsdom environment:

    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']});