How Do I Set a Mock Date in Jest?
I'm Using Moment. Js to Do Most of My Date Logic in a Helper File for My React Components but I Haven't Been Able to Figure out How to Mock a Date in Jest a La...
I'm using moment.js to do most of my date logic in a helper file for my React components but I haven't been able to figure out how to mock a date in Jest a la sinon.useFakeTimers().
The Jest docs only speak about timer functions like setTimeout, setInterval etc but don't help with setting a date and then checking that my date functions do what they're meant to do.
Here is some of my JS file:
var moment = require('moment');
var DateHelper = {
DATE_FORMAT: 'MMMM D',
API_DATE_FORMAT: 'YYYY-MM-DD',
formatDate: function(date) {
return date.format(this.DATE_FORMAT);
},
isDateToday: function(date) {
return this.formatDate(date) === this.formatDate(moment());
}
};
module.exports = DateHelper;
and here is what I've set up using Jest:
jest.dontMock('../../../dashboard/calendar/date-helper')
.dontMock('moment');
describe('DateHelper', function() {
var DateHelper = require('../../../dashboard/calendar/date-helper'),
moment = require('moment'),
DATE_FORMAT = 'MMMM D';
describe('formatDate', function() {
it('should return the date formatted as DATE_FORMAT', function() {
var unformattedDate = moment('2014-05-12T00:00:00.000Z'),
formattedDate = DateHelper.formatDate(unformattedDate);
expect(formattedDate).toEqual('May 12');
});
});
describe('isDateToday', function() {
it('should return true if the passed in date is today', function() {
var today = moment();
expect(DateHelper.isDateToday(today)).toEqual(true);
});
});
});
Now these tests pass because I'm using moment and my functions use moment but it seems a bit unstable and I would like to set the date to a fixed time for the tests.
Any idea on how that could be accomplished?
21 Answers
As of Jest 26 this can be achieved using "modern" fake timers without needing to install any 3rd party modules:
jest
.useFakeTimers()
.setSystemTime(new Date('2020-01-01'));
If you want the fake timers to be active for all tests, you can set timers: 'modern' in your configuration:
EDIT: As of Jest 27 modern fake timers is the default, so you can drop the argument to useFakeTimers.
Since momentjs uses Date internally, you can just overwrite the Date.now function to always return the same moment.
Date.now = jest.fn(() => 1487076708000) //14.02.2017
or
Date.now = jest.fn(() => new Date(Date.UTC(2017, 1, 14)).valueOf())
For quick and dirty solution use jest.spyOn for locking time:
let dateNowSpy;
beforeAll(() => {
// Lock Time
dateNowSpy = jest.spyOn(Date, 'now').mockImplementation(() => 1487076708000);
});
afterAll(() => {
// Unlock Time
dateNowSpy.mockRestore();
});
UPDATE:
For a more robust solution look at timekeeper:
import timekeeper from 'timekeeper';
beforeAll(() => {
// Lock Time
timekeeper.freeze(new Date('2014-01-01'));
});
afterAll(() => {
// Unlock Time
timekeeper.reset();
});
MockDate can be used in jest tests to change what new Date() returns:
var MockDate = require('mockdate');
// I use a timestamp to make sure the date stays fixed to the ms
MockDate.set(1434319925275);
// test code here
// reset to native Date()
MockDate.reset();
For those who want to mock methods on a new Date object you can do the following:
beforeEach(() => {
jest.spyOn(Date.prototype, 'getDay').mockReturnValue(2);
jest.spyOn(Date.prototype, 'toISOString').mockReturnValue('2000-01-01T00:00:00.000Z');
});
afterEach(() => {
jest.restoreAllMocks()
});
jest-date-mock is a complete javascript module wrote by me, and it is used to test Date on jest.
import { advanceBy, advanceTo } from 'jest-date-mock';
test('usage', () => {
advanceTo(new Date(2018, 5, 27, 0, 0, 0)); // reset to date time.
const now = Date.now();
advanceBy(3000); // advance time 3 seconds
expect(+new Date() - now).toBe(3000);
advanceBy(-1000); // advance time -1 second
expect(+new Date() - now).toBe(2000);
clear();
Date.now(); // will got current timestamp
});
Use the only 3 api for test cases.
- advanceBy(ms): advance date timestamp by ms.
- advanceTo([timestamp]): reset date to timestamp, default to 0.
- clear(): shut down the mock system.
Here are a few readable ways for different use cases. I prefer using spies over saving references to the original objects, which can be accidentally overwritten in some other code.
Must Read
One-off mocking
jest
.spyOn(global.Date, 'now')
.mockImplementationOnce(() => Date.parse('2020-02-14'));