Node.js test runner: mock timers don't actually advance time
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...