Node.js test runner: mock timers don't actually advance time

Nodejs·Sep 13, 2026·intermediate·
Quick answer

The built-in node:test runner's mock timers need to be enabled for the specific APIs you're using (`t.mock.timers.enable({ apis: ['setTimeout'] })`), and you must explicitly call `t.mock.timers.tick(ms)` to advance them — otherwise real timers keep running and your assertions just time out.

What causes this error

Node's built-in node:test runner ships mock timer support, but it's opt-in per API. Calling t.mock.timers.enable() with no arguments only mocks a default subset, and forgetting to call .tick(ms) afterward means fake timers just sit there — any code awaiting a real setTimeout will hang until the test's own timeout fires.

The Fix

import { test } from 'node:test';
import assert from 'node:assert';

test('debounced function fires after delay', (t) => {
  t.mock.timers.enable({ apis: ['setTimeout'] });

  let called = false;
  setTimeout(() => { called = true; }, 1000);

  assert.equal(called, false);
  t.mock.timers.tick(1000); // manually advance the fake clock
  assert.equal(called, true);
});

Common causes / variations

  • Calling `.enable()` without listing the timer API actually used in the code under test (e.g. `setInterval` vs `setTimeout`)
  • Forgetting `.tick()` entirely and expecting time to advance automatically
  • Mixing real async I/O (a real network call) with mocked timers in the same test, causing unpredictable ordering

Related errors

See also: UnhandledPromiseRejectionWarning in Express route handlers.

Was this fix helpful?

Comments

Loading comments...