Jest · Unit tests that survive refactors
How do you test code that uses timers?
Short answer
Call jest.useFakeTimers() to replace timer functions with controllable fakes, advance them with jest.advanceTimersByTime, and switch back to real timers in afterEach.
Real timers make tests slow and flaky: a debounce of one second would freeze the suite, and a background interval can fire after the test finishes. Fake timers replace setTimeout, setInterval, Date and related APIs with a virtual clock that the test controls.
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
test("shows the saved indicator after the debounce", () => {
const onSave = jest.fn();
const save = debounce(onSave, 500);
save("draft");
expect(onSave).not.toHaveBeenCalled();
jest.advanceTimersByTime(500);
expect(onSave).toHaveBeenCalledWith("draft");
});The timer API
jest.advanceTimersByTime(ms)moves the virtual clock forwardjest.runAllTimers()drains the queue, which can loop on recursive timersjest.runOnlyPendingTimers()runs what is currently queuedjest.setSystemTime(date)fixesDate.nowfor deterministic datesjest.useFakeTimers()can be scoped withjest.useFakeTimers({ doNotFake: [...] })
Modern fake timers also fake Date and performance, which is usually what you want for deterministic snapshots and time-based logic. They also affect queueMicrotask and process.nextTick depending on configuration. If a test only needs a fixed date, jest.setSystemTime is often simpler than faking every timer.
act for React, and finish pending microtasks before asserting. Always restore real timers so a later test does not inherit a frozen clock.Follow-ups you should expect
Interviewers use these to check whether the first answer was understanding or memorisation.
- 1What is the difference between runAllTimers and runOnlyPendingTimers?
- 2How do you test a debounce without waiting?
- 3Why should fake timers be restored in afterEach?
- 4How do you freeze the current date in a test?
Say it in the interview
I switch to fake timers before a test that involves setTimeout, setInterval or Date, then advance the virtual clock to trigger the callback. advanceTimersByTime is my default because it is explicit and does not accidentally drain recursive timers. I set the system time when the value of Date matters. In afterEach I always return to real timers, otherwise the next test inherits a frozen clock.
Use this as a model, not a script. Saying it in your own words is what makes it survive follow-up pressure.
Related questions
Reading is step one
Now say it out loud.
Understanding an answer and delivering it under pressure are different skills. Rehearse this topic in a live Jest mock interview and get scored feedback on depth, clarity and confidence.