0%

jest-useFakeTimer

Jest useFakeTimer

Jest’s useFakeTimers function is used to mock timers in JavaScript. This is particularly useful when you have code that uses setTimeout, setInterval, or Date objects, and you want to control the passage of time in your tests.

Basic Usage

To use Jest’s useFakeTimers function, you need to call it before the code you want to test. You can then advance the timers using the advanceTimersByTime function.

Advance Timers

待测试代码:

1
2
3
4
5
6
7
somethingDone = false;

doSomethingAfter1Second() {
setTimeout(() => {
this.somethingDone = true;
}, 1000);
}

测试代码:

1
2
3
4
5
6
7
8
9
10
it('test doSomethingAfter1Second', () => {
jest.useFakeTimers();

const component = new Component();
component.doSomethingAfter1Second();
expect(component.somethingDone).toBe(false);

jest.advanceTimersByTime(1000);
expect(component.somethingDone).toBe(true);
});

Run all timers

Jest.runAllTimers() will run all pending timers. This is useful when you have multiple timers that need to be run in sequence. (Note, it will not work for nested timers)

待测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
somethingDone = false;

doSomethingAfter1Second() {
setTimeout(() => {
this.somethingDone = true;
}, 1000);

setTimeout(() => {
this.somethingDone = false;
}, 2000);

// other timers...
}

测试代码:

1
2
3
4
5
6
it('test doSomethingAfter1Second', () => {
jest.useFakeTimers();
component.doSomethingAfter1Second();
jest.runAllTimers();
expect(component.somethingDone).toBe(false);
});

Run only pending timers

There are also scenarios where you might have a recursive timer – that is a timer that sets a new timer in its own callback.

Jest.runOnlyPendingTimers() will only run the timers that are currently pending. This is useful when you want to run the timers that are currently in the queue, but not the ones that are scheduled to run in the future.

If you use jest.runAllTimers here, it will run all the timers and end up with an infinite loop.

待测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
firstRound = false;
secondRound = false;

infiniteTimerGame() {
setTimeout(() => {
this.firstRound = true;
// Schedule the next game in 10 seconds
setTimeout(() => {
this.secondRound = true;
this.infiniteTimerGame();
}, 10000);
}, 1000);
}

测试代码:

1
2
3
4
5
6
7
8
9
10
11
it('test infiniteTimerGame', () => {
jest.useFakeTimers();
component.infiniteTimerGame();
// This will only run the timers that are currently pending(the outer timer)
jest.runOnlyPendingTimers();
expect(component.firstRound).toBe(true);

// or jest.runOnlyPendingTimers(); This will run the newly created timer
jest.advanceTimersByTime(10000);
expect(component.secondRound).toBe(true);
});

Selective faking

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
3
4
5
6
7
8
const mockPerformanceMark = jest.fn();
window.performance.mark = mockPerformanceMark;

test('allows mocking `performance.mark()`', () => {
jest.useFakeTimers({doNotFake: ['performance']});

expect(window.performance.mark).toBe(mockPerformanceMark);
});

Restore timers

After you have finished testing, you should restore the original timers using jest.useRealTimers(), This can be done by invoking jest.useRealTimers() in the afterEach hook.

1
2
3
afterEach(() => {
jest.useRealTimers();
});

You can also do this at the end of a test:

1
2
3
4
5
6
7
8
9
10
11
12
it('test doSomethingAfter1Second', () => {
jest.useFakeTimers();

const component = new Component();
component.doSomethingAfter1Second();
expect(component.somethingDone).toBe(false);

jest.advanceTimersByTime(1000);
expect(component.somethingDone).toBe(true);

jest.useRealTimers(); // restore to use real timers
});