Skip to content

Jest Fake Timers Not Advancing Fix

DodaTech Updated 2026-06-24 2 min read

In this tutorial, you'll learn about Jest Fake Timers Not Advancing Fix. We cover key concepts, practical examples, and best practices.

Your setTimeout callback never runs during the test — even after calling jest.advanceTimersByTime(), the timer doesn't fire.

The Problem

// WRONG — fake timers enabled after the timer is already set
const callback = jest.fn();

setTimeout(callback, 1000);
jest.useFakeTimers();
jest.advanceTimersByTime(1000);

expect(callback).toHaveBeenCalled(); // FAILS
Expected number of calls: 1
Received number of calls: 0

The timer was created using real timers because jest.useFakeTimers() was called after setTimeout. Fake timers only affect timers created after they're enabled.

Step-by-Step Fix

1. Enable fake timers before timer creation

// RIGHT — enable fake timers first
jest.useFakeTimers();

const callback = jest.fn();
setTimeout(callback, 1000);
jest.advanceTimersByTime(1000);

expect(callback).toHaveBeenCalled();

2. Use jest.runAllTimers for immediate execution

// RIGHT — run all pending timers
jest.useFakeTimers();

const callback = jest.fn();
setInterval(callback, 500);

jest.runAllTimers();
expect(callback).toHaveBeenCalled();

3. Advance timers with granular control

// RIGHT — step through timers
jest.useFakeTimers();

const fn = jest.fn();
setTimeout(() => fn('step1'), 100);
setTimeout(() => fn('step2'), 200);

jest.advanceTimersByTime(150);
expect(fn).toHaveBeenCalledWith('step1');
expect(fn).not.toHaveBeenCalledWith('step2');

jest.advanceTimersByTime(50);
expect(fn).toHaveBeenCalledWith('step2');

4. Configure fake timers with modern API

// RIGHT — modern fake timers configuration
jest.useFakeTimers({
  legacyFakeTimers: false,
  advanceTimers: false,
});

const fn = jest.fn();
setTimeout(fn, 1000);
jest.advanceTimersByTime(1000);
expect(fn).toHaveBeenCalled();

Expected output:

PASS  tests/timer.test.js
  ✓ advances timers (8 ms)
  ✓ runs all timers (3 ms)
  ✓ granular control (5 ms)

Prevention Tips

  • Call jest.useFakeTimers() at the top of tests or in beforeEach
  • Use jest.advanceTimersByTime() for precise timer control
  • Use jest.runAllTimers() to execute all pending timers
  • Restore real timers with jest.useRealTimers() in afterEach
  • Use jest.clearAllTimers() to remove pending timers

Common Mistakes with mock timer

  1. Using head and tail instead of pattern matching, causing runtime errors on empty lists
  2. Forgetting that lazy evaluation defers computation until the value is forced, causing space leaks with unevaluated thunks
  3. Using return to exit a function early instead of wrapping a pure value in the monad

These mistakes appear frequently in real-world JEST code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### Should I use legacy or modern fake timers?

Use modern fake timers (default in Jest 27+). Legacy fake timers are available as jest.useFakeTimers({ legacyFakeTimers: true }) for backward compatibility. Modern timers better match browser behavior.

How do I test recursive setTimeout patterns?

Use jest.advanceTimersByTime() in a loop or jest.runAllTimers() which handles nested timers. Be careful with infinite recursion — set a limit with jest.runAllTimers({ timeout: 5000 }).

Does jest.useRealTimers clean up between tests?

Yes, but it's better to use afterEach(() => { jest.useRealTimers(); }) explicitly. This ensures one test's fake timer configuration doesn't leak into the next test.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro