Mocking/stubbing the current Date in Jest tests
There are situations where new Date()
or Date.now
is used in application code. That code needs to be tested, and it’s always a struggle to remember how to mock/stub or spy on Date.now
/new Date
with Jest.
This post goes through multiple approaches to mocking, stubbing and spying on the date constructor using Jest.
There’s a full examples repository at github.com/HugoDF/jest-handbook-examples.
Table of Contents
On using Date.now vs new Date()
Date.now()
returns the unix time, ie. “the number of milliseconds elapsed since January 1, 1970 00:00:00 UTC.” (see Date.now on MDN).
new Date()
returns a new Date object, and behaves differently based on the input passed to it. If called with nothing, it returns the current Date.
The following are equivalent, although the values stored in the two variables will be strictly different.
const now = new Date();
const explicitNow = new Date(Date.now());
The values are strictly different because the “now” is calculated at different times, but since the Date constructor (new Date()
) supports passing a unix time to it, the two are equivalent.
Using new Date(Date.now())
makes for code that is a lot easier to test. Mocking a function that returns a number (like Date.now) is a lot easier than mocking a constructor.
For example the full code of the jest-mock-now
package is the following (see the code at github.com/mattiaerre/jest-mock-now):
const { NOW } = require('./constants');
module.exports = date => {
const now = date ? date.getTime() : NOW;
Date.now = jest.spyOn(Date, 'now').mockImplementation(() => now);
return now;
};
Subscribe to the Enterprise Node.js and JavaScript newsletter for future posts about mocking new Date() and other constructors.
Using jest.setSystemTime
This functionality is only available when using Jest with “modern” timers (ie. not legacy). “modern” timers are opt-in from Jest 26 and the default from Jest 27.
const date = new Date('2023-05-14');
jest.useFakeTimers().setSystemTime(date);
expect(new Date()).toEqual(date);
Full docs: https://jestjs.io/docs/jest-object#jestsetsystemtimenow-number--date
Using native node:test
MockTimers
Note: experimental, requires Node 20+ and won’t work in Jest tests, but Node.js now has a native API that mocks the Date.
import { mock } from 'node:test';
mock.timers.enable({ apis: ['Date'], now: new Date('2023-05-14') });
// later
mock.timers.setTime(new Date('2023-05-14').valueOf());
I’ve documented it along with other features in: Mocking/stubbing the Date and timers (setTimeout) in Node.js tests with built-in node:test
MockTimers
Full docs: https://nodejs.org/api/test.html#dates
Replacing Date.now with a stub
const literallyJustDateNow = () => Date.now();
test('It should call and return Date.now()', () => {
const realDateNow = Date.now.bind(global.Date);
const dateNowStub = jest.fn(() => 1530518207007);
global.Date.now = dateNowStub;
expect(literallyJustDateNow()).toBe(1530518207007);
expect(dateNowStub).toHaveBeenCalled();
global.Date.now = realDateNow;
});
This isn’t really a Jest-specific trick, we’re just accessing Node global
object and replace Date.now
with a stub.
We’re also being good unit-testing citizens and putting the original global.Date.now
implementation back 😇.
Spy on Date.now and add a mock implementation
A terser implementation of a similar test would be using jest.spyOn(global.Date, 'now').mockImplementation()
.
Our mockImplementation will use a hard-coded date initialised using new Date('valid-date-string')
and return valueOf()
, which corresponds to the unix time of that date.
const getNow = () => new Date(Date.now());
test('It should create correct now Date', () => {
jest
.spyOn(global.Date, 'now')
.mockImplementationOnce(() =>
new Date('2019-05-14T11:01:58.135Z').valueOf()
);
expect(getNow()).toEqual(new Date('2019-05-14T11:01:58.135Z'));
});
This has the advantage of not having to deal with replacing the real date or putting it back.
Mock the whole Date class with a fixed date instance
Credit to Jamie Webb (webb04) who mentions this solution in the “Mocking current time for Date” Jest issue.
const getCurrentDate = () => new Date();
let realDate;
test('It should create new date', () => {
// Setup
const currentDate = new Date('2019-05-14T11:01:58.135Z');
realDate = Date;
global.Date = class extends Date {
constructor(date) {
if (date) {
return super(date);
}
return currentDate;
}
};
expect(getCurrentDate()).toEqual(new Date('2019-05-14T11:01:58.135Z'));
// Cleanup
global.Date = realDate;
});
Like we mentioned in the introduction, mocking the whole class is very heavy-handed.
Spy on new Date() constructor and add a mock implementation
Credit to Paul Melero (gangsthub) who mentions this solution in the “Mocking current time for Date” Jest issue.
const getCurrentDate = () => new Date();
test('It should create new date', () => {
jest
.spyOn(global, 'Date')
.mockImplementationOnce(() => new Date('2019-05-14T11:01:58.135Z'));
expect(getCurrentDate()).toEqual(new Date('2019-05-14T11:01:58.135Z'));
});
This is nice and terse but relies on JavaScript hoisting madness and knowing that Jest allows you to mock global.Date
.
Full examples repository at github.com/HugoDF/jest-handbook-examples.
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