How to mock a constructor like new Date()

JavascriptJestjs

Javascript Problem Overview


I have a method which depends on new Date to create a date object and then manipulates it. I'm testing that the manipulation works as expected, so I need to compare the returned date with expected date. In order to do that I need to make sure that new Date returns the same value in the test and in the method being tested. How can I do that?

Is there a way to actually mock the return value of a constructor function?

I could create a module that can be required with a function that provides a date object and can be mocked. But that seems like an unnecessary abstraction in my code.

an example function to be tested...

module.exports = {
  sameTimeTomorrow: function(){
    var dt = new Date();
        dt.setDate(dt + 1);
    return dt;
  }
};

how do I mock the return value of new Date()?

Javascript Solutions


Solution 1 - Javascript

Since jest 26, you can use the 'modern' fakeTimers implementation (see article here) wich supports the method jest.setSystemTime.

beforeAll(() => {
    jest.useFakeTimers('modern');
    jest.setSystemTime(new Date(2020, 3, 1));
});

afterAll(() => {
    jest.useRealTimers();
});

Note that 'modern' will be the default implementation from jest version 27.

See documentation for setSystemTime here.

Solution 2 - Javascript

Update: this answer is the approach for jest < version 26 see this answer for recent jest versions.


You can mock a constructor like new Date() using jest.spyOn as below:

test('mocks a constructor like new Date()', () => {
  console.log('Normal:   ', new Date().getTime())

  const mockDate = new Date(1466424490000)
  const spy = jest
    .spyOn(global, 'Date')
    .mockImplementation(() => mockDate)

  console.log('Mocked:   ', new Date().getTime())
  spy.mockRestore()

  console.log('Restored: ', new Date().getTime())
})

And the output looks like:

Normal:    1566424897579
Mocked:    1466424490000
Restored:  1566424897608

See the reference project on GitHub.

Note: If you are using TypeScript and you would encounter a compilation error, Argument of type '() => Date' is not assignable to parameter of type '() => string'. Type 'Date' is not assignable to type 'string'. In this case, a workaround is to use the mockdate library, which can be used to change when "now" is. See this question for more details.

Solution 3 - Javascript

You can use jasmine's spyOn (jest is built on jasmine) to mock Date's prototype for getDate as follows:

spyOn(Date.prototype, 'setDate').and.returnValue(DATE_TO_TEST_WITH);

SpyOn will also clean up after it's self and only lasts for the scope of the test.

Solution 4 - Javascript

Although the other answers solve the problem, I find it more natural and generally applicable to mock only the Date's "parameterless constructor" behavior while keeping other features of Date intact. For instance, when ISO Date string is passed to the constructor, it is probably reasonable to expect that this specific date is returned as opposed to the mocked Date.

test('spies new Date(...params) constructor returning a mock when no args are passed but delegating to real constructor otherwise', () => {
    const DateReal = global.Date;
    const mockDate = new Date("2020-11-01T00:00:00.000Z");

    const spy = jest
        .spyOn(global, 'Date')
        .mockImplementation((...args) => {
            if (args.length) {
                return new DateReal(...args);
            }
            return mockDate;
        })
        
    const dateNow = new Date();

    //no parameter => mocked current Date returned
    console.log(dateNow.toISOString()); //outputs: "2020-11-01T00:00:00.000Z"

    //explicit parameters passed => delegated to the real constructor
    console.log(new Date("2020-11-30").toISOString()); //outputs: "2020-11-30T00:00:00.000Z"
    
    //(the mocked) current Date + 1 month => delegated to the real constructor
    let dateOneMonthFromNow = new Date(dateNow);
    dateOneMonthFromNow.setMonth(dateNow.getMonth() + 1);
    console.log(dateOneMonthFromNow.toISOString()); //outputs: "2020-12-01T00:00:00.000Z"

    spy.mockRestore();
}); 

Solution 5 - Javascript

You can override Date constructor with an mocked function which returns your a constructed Date object with a date value you specified:

var yourModule = require('./yourModule')

test('Mock Date', () => {
  const mockedDate = new Date(2017, 11, 10)
  const originalDate = Date

  global.Date = jest.fn(() => mockedDate)
  global.Date.setDate = originalDate.setDate

  expect(yourModule.sameTimeTomorrow().getDate()).toEqual(11)
})

You can test the example here: https://repl.it/@miluoshi5/jest-mock-date

Solution 6 - Javascript

You can replace the Date constructor with something that always returns a hardcoded date, and then put it back to normal when done.

var _Date = null;

function replaceDate() {
  if (_Date) {
    return
  };

  _Date = Date;
  
  Object.getOwnPropertyNames(Date).forEach(function(name) { 
    _Date[name] = Date[name] 
  });
  
  // set Date ctor to always return same date
  Date = function() { return new _Date('2000-01-01T00:00:00.000Z') }
  
  Object.getOwnPropertyNames(_Date).forEach(function(name) { 
    Date[name] = _Date[name] 
  });  
}

function repairDate() {
  if (_Date === null) {
    return;
  }

  Date = _Date;
  Object.getOwnPropertyNames(_Date).forEach(function(name) { 
    Date[name] = _Date[name] 
  });  

  _Date = null;
}

// test that two dates created at different times return the same timestamp
var t0 = new Date();

// create another one 100ms later
setTimeout(function() {
  var t1 = new Date();

  console.log(t0.getTime(), t1.getTime(), t0.getTime() === t1.getTime());

  // put things back to normal when done
  repairDate();
}, 100);

Solution 7 - Javascript

If you have more than one Date (either in multiple tests or multiple times in one test) you might need to do the following:

const OriginalDate = Date;

it('should stub multiple date instances', () => {
  jest.spyOn(global, 'Date');
  const date1: any = new OriginalDate(2021, 1, 18);
  (Date as any).mockImplementationOnce(mockDate(OriginalDate, date1));

  const date2: any = new OriginalDate(2021, 1, 19);
  (Date as any).mockImplementationOnce(mockDate(OriginalDate, date2));

  const actualDate1 = new Date();
  const actualDate2 = new Date();

  expect(actualDate1).toBe(date1);
  expect(actualDate2).toBe(date2);
});

function mockDate(OriginalDate: DateConstructor, date: any): any {
  return (aDate: string) => {
    if (aDate) {
      return new OriginalDate(aDate);
    }
    return date;
  };
}

Also see this answer


Original Answer:

I just wrote a jest test and was able to stub new Date() with global.Date = () => now

Solution 8 - Javascript

You can use date-faker to mock what new Date() or Date.now() returns.

import { dateFaker } from 'date-faker'; // var { dateFaker } = require('date-faker');

// will return tomorrow, shift by one unit
dateFaker.add(1, 'day'); 

// shift by several units
dateFaker.add({ year: 1, month: -2, day: 3 });

// set up specific date, accepts Date or time string
dateFaker.set('2019/01/24'); 

dateFaker.reset();

Solution 9 - Javascript

Simply do this:

it('should mock Date and its methods', () => {
    const mockDate = new Date('14 Oct 1995')
    global.Date = jest.fn().mockImplementation(() => mockDate)
    Date.prototype.setHours = jest.fn().mockImplementation((hours) => hours)
    Date.prototype.getHours = jest.fn().mockReturnValue(1)
}

it's working for me

Solution 10 - Javascript

In my case I had to mock the whole Date and 'now' function before test:

const mockedData = new Date('2020-11-26T00:00:00.000Z');

jest.spyOn(global, 'Date').mockImplementation(() => mockedData);

Date.now = () => 1606348800;

describe('test', () => {...})

Solution 11 - Javascript

I'm using Typescript and the easiest implementaion I found was doing the following:

const spy = jest.spyOn(global, 'Date');  // spy on date
const date = spy.mock.instances[0];      // gets the date in string format

and then use new Date(date) for your tests

Solution 12 - Javascript

Here's what I'm doing now and this is working and doesn't clutter my method's signature.

newDate.js

module.exports = function(){
  return new Date();
};

someModule.js

var newDate = require('newDate.js');
module.exports = {
  sameTimeTomorrow: function(){
    var dt = newDate();
        dt.setDate(dt.getDate() + 1);
    return dt;
  }
};

someModule-test.js

jest.dontMock('someModule.js');

describe('someModule', function(){

  it('sameTimeTomorrow', function(){
   var newDate = require('../../_app/util/newDate.js');
       newDate.mockReturnValue(new Date(2015, 02, 13, 09, 15, 40, 123));

   var someModule = require('someModule.js');

   expect(someModule.sameTimeTomorrow().toString()).toBe(new Date(2015, 02, 14, 09, 15, 40, 123).toString());
  });

});

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSeth FeldkampView Question on Stackoverflow
Solution 1 - JavascriptThdKView Answer on Stackoverflow
Solution 2 - JavascriptYuciView Answer on Stackoverflow
Solution 3 - JavascriptlinuxdanView Answer on Stackoverflow
Solution 4 - Javascriptmatej bobalyView Answer on Stackoverflow
Solution 5 - JavascriptMildoView Answer on Stackoverflow
Solution 6 - JavascriptBen TaberView Answer on Stackoverflow
Solution 7 - JavascriptDavidView Answer on Stackoverflow
Solution 8 - JavascriptMatGarView Answer on Stackoverflow
Solution 9 - JavascriptDuc Trung MaiView Answer on Stackoverflow
Solution 10 - JavascriptRiddView Answer on Stackoverflow
Solution 11 - JavascriptHarry RileyView Answer on Stackoverflow
Solution 12 - JavascriptSeth FeldkampView Answer on Stackoverflow