/ #JavaScript #jest 

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-mock-date-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.

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-mock-date-examples.

unsplash-logoBryce Barker

Looking for a new job? Take Triplebyte’s quiz and have top tech companies pitch you!

Author

Hugo Di Francesco

A Software Engineer who is big on Node.js, queues and Vue(s). Co-author of "Professional JavaScript" with Packt. He shares practical JavaScript tips for the developer who wants to get things done on Code with Hugo. University College London (UCL), MEng Mathematical Computation Graduate.

Get Testing Superpowers with these Underused Jest Features

Subscribe for free resources that turbocharge your Jest tests and a discount on the "Advanced Jest Handbook"