Since setTimeout(), setInterval(), clearTimeout(), clearInterval()
are not ideal for testing environment because they are depended on real time to elapse. But Jest comes with some handy methods to do that.
1. Fake timers
By calling jest.useFakeTimers()
, Jest will replace the original implementation of the above functions.
// timer.ts
const timerExample = (timeout: number, callback: (T?: any) => void) => {
setTimeout(() => {
callback();
}, timeout);
};
// timer.test.ts
jest.useFakeTimers();
jest.spyOn(global, "setTimeout");
import { timerExample } from "./../src/examples/timer";
describe("timer", () => {
it("testing timer", () => {
timerExample(100, () => {});
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 100);
});
});
We can stimulate the timing by using:
jest.runAllTimers()
: will fast-forward until all timers have been executed.jest.advanceTimersByTime(msToRun)
: all timers are advanced by msToRun milliseconds.- required to use
jest.useFakeTimers();
- all “macro-tasks” that have been queued via
setTimeout()
orsetInterval()
, and would be executed during this time frame, will be executed.
- required to use
// timer.test.ts
describe("timer", () => {
it("testing timer", () => {
const callback = jest.fn();
timerExample(100, callback);
expect(callback).not.toHaveBeenCalledTimes(1);
jest.runAllTimers();
expect(callback).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 100);
});
});
2. Spy on the global Date
Sometimes, we might need to use:
jest.spyOn(global.Date, "now").mockReturnValueOnce(Date.now() + 1000);
to make the time relate to Date.
it("should calculate duration correctly", async () => {
videoRecording.start();
jest.spyOn(global.Date, "now").mockReturnValueOnce(Date.now() + 2000);
videoRecording.stop();
expect(videoRecording.duration).toBe(2000); // Duration should be 2 seconds
videoRecording.reset();
});
Refs: