DEV Community

Cover image for How to mock Date with Jest
Maxence Poutord
Maxence Poutord

Posted on • Updated on • Originally published at maxpou.fr

How to mock Date with Jest

I spent too many hours trying to mock correctly the JavaScript's Date object. I tried a few things I've found on Google... without success. I finally ended up with a kinda handcrafted solution.

Every time we mock, we diverge from the real world scenario.

For this reason, I tend not to mock... but sometimes, there are not many choices. Fortunately, Date is one good exception!

Everybody on the same timezone!

To prevent problems related to timezones (e.g. date formating), you can set node timezone in jest config file. Now you are sure all tests are executed on the same timezone no matter where your colleagues or your CI server are.

// jest.config.js
process.env.TZ = 'GMT';

module.exports = {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

See also: the full list of timezones (column TZ database name)

Mock Date.now

Let's say I want to test a dashboard component which tells me "hello" with the date of the day. The lazy way is to only test the Hello part (without the date). Because it changes every day. But you won't test the date formatting part.

If you want to, you gonna have to mock Date.now() and put a default one.

// your-test.spec.js

const RealDate = Date.now

beforeAll(() => {
  global.Date.now = jest.fn(() => new Date('2019-04-07T10:20:30Z').getTime())
})

afterAll(() => {
  global.Date.now = RealDate
})
Enter fullscreen mode Exit fullscreen mode

Now in the same file, you can add something like the following:

it('should show a formatted date of today', async () => {
  const dashboard = await Mount(<Dashboard />)
  expect(dashboard).toHaveText('Hi Max, today is 7 April 2019')
})
Enter fullscreen mode Exit fullscreen mode

💡RealDate store "real" Date instance, so we can put it back afterwards.

Using Moment.js?

You are probably using the very popular moment.js library. If so, mocking Date.now, will probably not be enough.
A workaround is to mock the entire node module.

// <root>/__mocks__/moment.js
const moment = jest.requireActual('moment');

Date.now = () => new Date('2019-04-07T10:20:30Z').getTime();

module.exports = moment;
Enter fullscreen mode Exit fullscreen mode

With this solution, you don't need beforeAll()/afterAll() listener. This mock will be effective for all tests.
And, every time moment() is called, return date will be the same 🎉

The very first thing I recommend you to do is to set a default Timezone.

// jest.config.js
process.env.TZ = 'GMT';

module.exports = {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Mock Date object

// your-test.spec.js

const RealDate = Date;

beforeEach(() => {
  global.Date.now = jest.fn(() => new Date('2019-04-22T10:20:30Z').getTime());
});

afterEach(() => {
  global.Date = RealDate;
});
Enter fullscreen mode Exit fullscreen mode

Using Moment?

If you're using the popular moment.js library, mocking Date.now, will probably not be enough. If you check the moment's code source, you will see that
you have to mock JavaScript Date object through a different way.

// <root>/__mocks__/moment.js
const moment = jest.requireActual('moment');

Date.now = () => new Date('2019-04-22T10:20:30Z').getTime();

module.exports = moment;
Enter fullscreen mode Exit fullscreen mode

Thank you for taking the time to read this post. I hope you found it useful! If you liked it, please give it a ❤️ or a 🦄! Also, feel free to comment or ask questions in the section below or on Twitter @_maxpou :)


Originally published on maxpou.fr.

Latest comments (11)

Collapse
 
andrecrimberg profile image
André Crimberg

Thanks for the process.env.TZ = 'GMT' tip 🙏🏽

Collapse
 
vladqqqqoo profile image
Vladislav

Great job!
Thank you a lot, it really helped.

Collapse
 
clint profile image
clint

Thanks for sharing this--very helpful.

The approach described here worked well for me, but then I stumbled onto another option that uses Jest's own API (I don't believe was available when this post was originally written). You can now do something like this:

import { jest } from '@jest/globals';

// Tell Jest to use a different timer implementation. You can also
// configure this in your jest.config.js file. For more info see
// https://jestjs.io/docs/en/configuration#timers-string).
jest.useFakeTimers('modern');

jest.setSystemTime(new Date('04 Dec 1995 00:12:00 GMT').getTime());

console.log(new Date()); // "2014-01-09T00:00:00.000Z"

// Back to reality...
jest.useRealTimers();

console.log(new Date()); // prints the actual system time...
Enter fullscreen mode Exit fullscreen mode
Collapse
 
stanleysathler profile image
Stanley Sathler • Edited

Unfortunately jest.useFakeTimers seems to not work well with native Promises, which means you can't use it in an async call.

github.com/facebook/jest/issues/10221

Collapse
 
abhishridwivedi profile image
AbhishriDwivedi

setSystemTime is giving error- setSystemTime does not exist on jest

Collapse
 
rafaelrozon profile image
Rafael Rozon

Thank you for this!

Collapse
 
maxpou profile image
Maxence Poutord

Thanks! That's true, it was added last may with Jest 26 :)

jestjs.io/blog/2020/05/05/jest-26

Collapse
 
ryandowd profile image
ryandowd

Great! Just what I was looking for!

Collapse
 
avalander profile image
Avalander • Edited

I just want to point out that both Date and RealDate reference the same object, so when you replace the function Date.now, it's also changing RealDate.now and your global.Date = RealDate at the end is not doing what you think it's doing.

What you should do if you want to keep and reassign the original now function is keep a reference to that function:

const realNow = Date.now

Date.now = () => 1000

Date.now = realNow
Enter fullscreen mode Exit fullscreen mode
Collapse
 
angelocicero profile image
Angelo Cicero

Very helpful!

Collapse
 
maxpou profile image
Maxence Poutord • Edited

🙊Oh! You're completely right! Thank you so much, I will update my post!!! :)

(sorry for the late answer, I didn't saw the notification...)