Mocking/stubbing the Date and timers (setTimeout) in Node.js tests with built-in `node:test` MockTimers
MockTimers
is an experimental class in the node:test
module which concerns itself with allowing mocking of timers (setTimeout
, setInterval
, setImmediate
) and the Date
built-ins.
Table of Contents
The MockTimers API is available as part the the node:test
built-in module since Node 20.4.0 (for mocking timers only), and the API was changed to allow for mocking of the date in Node v21.2.0.
In this post we’ll look at mocking the Date using mock.timers.enable({ apis: ['Date']})
and then how to mock setTimeout
using the Node 20 syntax, mock.timers.enable(['setTimeout'])
.
To skip to the full examples use the following links:
- node-test-runner-examples/src/02.03-date-mock-timers.test.js
- node-test-runner-examples/src/02.03-timeout-mock-timers.test.js
Date mocking
Warning: The API demonstrated here is only available in Node 21.2.0+ and is still marked as experimental, so may change in Node.js versions without a major version bump. See stability Node.js docs.
The following code is available at node-test-runner-examples/src/02.03-date-mock-timers.test.js.
We can use mock.timers.enable({ apis: ['Date'], now: new Date() })
from the node:test
module to set a specific date in tests via the now
parameter. We can use mock.timers.reset()
to unmock the API.
A simple example of this is as follows:
import { test } from 'node:test';
import assert from 'node:assert/strict';
test('mock.timers can mock Date and be reset', (t) => {
t.mock.timers.enable({
apis: ['Date'],
now: new Date('2023-05-14T11:01:58.135Z'),
});
assert.deepEqual(new Date(), new Date('2023-05-14T11:01:58.135Z'));
t.mock.timers.reset();
assert(new Date().getFullYear() > 2023);
});
mock.timers
is available both from the “test context” as above (the parameter passed to the test implementation function) as t.mock.timers
but also from the node:test
mock
variable. We can therefore reset the Date mocking outside of the test context, for example in an afterEach hook.
mock.timers.enable
also affects the output of Date.now()
, and we can control the returned data after the mock.timers.enable
call with mock.timers.tick(milliseconds)
or mock.timers.setTime(milliseconds)
.
We demonstrate these in the following code snippet:
import { test, mock, afterEach } from 'node:test';
// no changes to other imports
afterEach(() => {
mock.timers.reset();
});
// no change to the other test
test('mock.timers can mock Date.now() and be ticked forward', (t) => {
t.mock.timers.enable({
apis: ['Date'],
now: new Date('2023-05-14T11:01:58.135Z'),
});
assert.deepEqual(Date.now(), new Date('2023-05-14T11:01:58.135Z').valueOf());
// advance by 24h
t.mock.timers.tick(1000 * 60 * 60 * 24);
assert.deepEqual(Date.now(), new Date('2023-05-15T11:01:58.135Z').valueOf());
t.mock.timers.setTime(20000);
assert.deepEqual(Date.now(), 20000);
});
Running the above tests with node
will yield the following output (since we’re using node:test
in our module, our file is will run as “Node Test Runner” and output accordingly):
node src/02.03-date-mock-timers.test.js
✔ mock.timers can mock Date and be reset (1.870625ms)
✔ mock.timers can mock Date.now() and be ticked forward (0.189166ms)
(node:2751) ExperimentalWarning: The MockTimers API is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
ℹ tests 2
ℹ suites 0
ℹ pass 2
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 7.150875
We’ve now seen how to use the Node 21+ MockTimers API to mock new Date()
and Date.now()
, next we’ll see how to use the Node 20+ MockTimers API to mock timers like setTimeout
.
Timer mocking setTimeout, setInterval
Warning: The API demonstrated here is only available in Node 20.4.0+ and is still marked as experimental, so may change in Node.js versions without a major version bump. See stability Node.js docs.
The following code is available at node-test-runner-examples/src/02.03-timeout-mock-timers.test.js.
setTimeout
, setInterval
and setImmediate
can be mocked in Node >20.2.0 by using mock.timers.enable(['setTimeout'])
followed by mock.timers.tick(milliseconds)
or mock.timers.runAll()
.
As demonstrated by the following code, we’ll have 2 “userland-promisified” timers with different delays, on resolution of each we will call a node:test
Mock function.
We’ll show how we can use mock.timers.tick
to advance the timers to the point where one of them resolves and how we can use mock.timers.runAll
to run all pending timers.
import { test, mock } from 'node:test';
import assert from 'node:assert/strict';
const wait = (ms) => new Promise((res) => setTimeout(res, ms));
test('mock.timers can mock setTimeout', async (t) => {
t.mock.timers.enable(['setTimeout']);
const fn = mock.fn();
const wait1 = wait(1000).then(() => fn());
const wait2 = wait(10000).then(() => fn());
assert.equal(fn.mock.callCount(), 0, 'fn should not have been called');
t.mock.timers.tick(1000);
await wait1; // await is required to flush promises
assert.equal(fn.mock.callCount(), 1, 'fn should have been called once');
t.mock.timers.runAll();
await wait2; // await is required to flush promises
assert.equal(fn.mock.callCount(), 2, 'fn should have been called twice');
});
Running the above tests with node
will yield the following output (since we’re using node:test
in our module, our file is will run as “Node Test Runner” and output accordingly):
node src/02.03-timeout-mock-timers.test.js
✔ mock.timers can mock setTimeout (2.241833ms)
(node:1878) ExperimentalWarning: The MockTimers API is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
ℹ tests 1
ℹ suites 0
ℹ pass 1
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 9.940875
We’ve now seen how to use the Node 20+ MockTimers API to mock timers like setTimeout
.
Get The Jest Handbook (100 pages)
Take your JavaScript testing to the next level by learning the ins and outs of Jest, the top JavaScript testing library.
orJoin 1000s of developers learning about Enterprise-grade Node.js & JavaScript