How do I set a mock date in Jest?
Asked Answered
D

30

346

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?

Dampier answered 18/4, 2015 at 16:3 Comment(1)
can you please select a different answer, since jest now has inbuilt date mocking?Gatian
E
466

As of Jest 26 this can be achieved using "modern" fake timers without needing to install any 3rd party modules: https://jestjs.io/blog/2020/05/05/jest-26#new-fake-timers

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: https://jestjs.io/docs/configuration#timers-string

EDIT: As of Jest 27 modern fake timers is the default, so you can drop the argument to useFakeTimers.

Expostulatory answered 12/8, 2020 at 13:2 Comment(10)
Also worth noting, the .useFakeTimers('modern') bit can be called from a global config file (like setupTests.js). So that in can be easily removed once that's the default option. From that same link: In Jest 27 we will swap the default to the new "modern".Flirtation
You may want to jest.useRealTimers() after you are done with the fake-timers.Romy
This solution is only working for me only if I call jest.setSystemTime() in the test setup; if I call it in beforeAll of my test suite, it is ignored. Check the repo I created for testing this github.com/dariospadoni/jestFakeTimersMock/blob/main/src/…Ragan
This is the most clean solution. BTW, you can also give a Date object to setSystemTime, no need to convert to epoch time.Below
As of Jest 27, you get this error TypeError: setSystemTime is not available when not using modern timers. Just remove .setSystemTime(...).Drenthe
jest 26: Expected 0 arguments, but got 1. for useFakeTimers('modern')Bani
this is good, this worked for me instead of useFakeTimers({now: new Date(xxxx)})Gwyn
Make sure you call this before rendering / mounting your component! :)Thaothapa
Weirdly, removing useFakeTimers did not work for me ([email protected]): ``` jest.useFakeTimers().setSystemTime(new Date('2021-08-16T12:00:00.000Z')); // worked jest.setSystemTime(new Date('2021-08-16T12:00:00.000Z')); // dates generated via 'getTime' will be the real date ```Readjust
I also needed to leave the useFakeTimers call in to get this to work with jest 29.7.0Valet
S
228

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())
Spillman answered 14/3, 2017 at 13:23 Comment(7)
Here's a little bit prettier method of setting the actual date that will be returned: Date.now = jest.fn(() => new Date(Date.UTC(2017, 0, 1)).valueOf());Emeldaemelen
Or even a little bit prettier:Date.now = jest.fn(() => +new Date('2017-01-01');Ginger
OR: Date.now = jest.fn(() => Date.parse('2017-02-14))Song
Will this properly mock all uses of Date? Like new Date()?Azurite
@EliasZamaria no. This answer does cover that use case though.Vitriolize
What about reverting it? So other tests can use Date.now()Liuka
@JeremyEaton missing a 'Theurer
S
162

For a robust solution, look at timekeeper:

import timekeeper from 'timekeeper';

beforeAll(() => {
    // Lock Time
    timekeeper.freeze(new Date('2014-01-01'));
});

afterAll(() => {
    // Unlock Time
    timekeeper.reset();
});

For older versions of Jest:

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();
});
Stantonstanway answered 12/12, 2017 at 20:45 Comment(8)
Great solution; no dependencies and keeping it resettable makes it easy to apply to a single test.Naevus
No need for dateNowSpy variable, and the mockReset() is redundant according to jestjs.io/docs/en/mock-function-api.html#mockfnmockrestore. In the afterAll, you can simply do Date.now.mockRestore()Bhagavadgita
this is great so then you don't need any additional libraries. But this will only really work if you're using static Date methods (which aren't many)Education
@Bhagavadgita Date.now.mockRestore(); gives a Property 'mockRestore' does not exist on type '() => number' errorScriptural
@Marco it should be jest.spyOn(Date, "now").mockRestore();Finegrained
I reckon it's more straightforward to use a variable for a spy. To make typescript happy use let dateNowSpy: jest.SpyInstance;.Mycenae
Useful and modernFaires
should be import * as timekeeper from 'timekeeper'; or var timekeeper = require('timekeeper'); in 2023.Kirsti
V
108

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();
Vouvray answered 14/6, 2015 at 22:23 Comment(2)
Worked great because I was using other functions of Date like valueOf().Shabby
Same i needed to mock the date, but also have Date.parse available and this works perfectly! I did this previously: dateSpy = jest.spyOn(global, 'Date').mockImplementation(() => new Date('1990-03-30T09:00:00')); but it stops the static methods on Date from working.Mitzvah
I
45

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()
});
Innovation answered 11/2, 2020 at 15:5 Comment(3)
This worked well for me, nice, simple and clean. Good suggestion!Tevis
What I like about this method is that it can be extended to mock any part of the Date prototype that you need to control as part of your tests. Plus it only relies on Jest for this feature, which is nice to keep the dependencies down.Handicraft
Short and simple, this worked for me.Carpus
B
10

This works for me:

const mockDate = new Date('14 Oct 1995')
global.Date = jest.fn().mockImplementation(() => mockDate) // mock Date "new" constructor
global.Date.now = jest.fn().mockReturnValue(mockDate.valueOf()) // mock Date.now
Bounteous answered 30/10, 2020 at 2:4 Comment(4)
it is fine, but not suitable for Typescript without applying @ts-ignoreAcidosis
I'm using NestJS, the code above works fine for me, no @ts-ignore neededBounteous
this worked for me, I was trying to mock "new Date()", thanks.Mooney
This worked for me and I'm using Typescript version 5.3.3 with Node 16Business
H
9

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.

One-off mocking

jest
  .spyOn(global.Date, 'now')
  .mockImplementationOnce(() => Date.parse('2020-02-14'));

A few tests

let dateSpy;

beforeAll(() => {
  dateSpy = jest
    .spyOn(global.Date, 'now')
    .mockImplementation(() => Date.parse('2020-02-14'));
});

afterAll(() => {
  dateSpy.mockRestore();
});
Harrell answered 2/6, 2020 at 13:12 Comment(0)
H
8

All the answer based only on the mock of Date.now() will not work everywhere since some packages (for instance moment.js) use new Date() instead.

In this context the answer based on MockDate is I think the only truly correct. If you don't want to use an external package, you can write directly in your beforeAll:

  const DATE_TO_USE = new Date('2017-02-02T12:54:59.218Z');
  // eslint-disable-next-line no-underscore-dangle
  const _Date = Date;
  const MockDate = (...args) => {
    switch (args.length) {
      case 0:
        return DATE_TO_USE;
      default:
        return new _Date(...args);
    }
  };
  MockDate.UTC = _Date.UTC;
  MockDate.now = () => DATE_TO_USE.getTime();
  MockDate.parse = _Date.parse;
  MockDate.toString = _Date.toString;
  MockDate.prototype = _Date.prototype;
  global.Date = MockDate;
Hildebrand answered 5/3, 2018 at 19:59 Comment(0)
H
8

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.
Halliburton answered 5/6, 2018 at 6:36 Comment(1)
what is your case?Halliburton
L
6

Since Jest version 29 you can do the following as well:

jest.useFakeTimers({
  now: 1673445238335,
});

The following options are allowed:

type FakeTimersConfig = {
  /**
   * If set to `true` all timers will be advanced automatically by 20 milliseconds
   * every 20 milliseconds. A custom time delta may be provided by passing a number.
   * The default is `false`.
   */
  advanceTimers?: boolean | number;
  /**
   * List of names of APIs that should not be faked. The default is `[]`, meaning
   * all APIs are faked.
   */
  doNotFake?: Array<FakeableAPI>;
  /**
   * Use the old fake timers implementation instead of one backed by `@sinonjs/fake-timers`.
   * The default is `false`.
   */
  legacyFakeTimers?: boolean;
  /** Sets current system time to be used by fake timers. The default is `Date.now()`. */
  now?: number | Date;
  /**
   * The maximum number of recursive timers that will be run when calling `jest.runAllTimers()`.
   * The default is `100_000` timers.
   */
  timerLimit?: number;
};

You can read more in the docs.

Lagunas answered 11/1, 2023 at 14:30 Comment(0)
C
5

This is how I mocked my Date.now() method to set the year to 2010 for my test

jest
  .spyOn(global.Date, 'now')
  .mockImplementationOnce(() => new Date(`2010`).valueOf());
Coadjutrix answered 18/2, 2020 at 6:22 Comment(1)
This is an excellent approach. I kind of combined a couple answers and put this at the top of one of my test files: jest.spyOn(global.Date, 'now').mockImplementation(() => 1487076708000);Markel
W
4

To mock toISOString you can do:

jest.spyOn(global.Date.prototype, 'toISOString').mockReturnValue('2023-09-06T11:54:47.050Z')
Wilhite answered 24/10, 2022 at 20:5 Comment(2)
for some reason I can't suggest a change, but your mock value isn't a real iso string, "2023-09-06T11:54:47.050Z" would be valid howeverIllboding
@HaroenViaene makes sense, amended thatWilhite
C
3

I'm using moment + moment-timezone and none of these worked for me.

This worked:

jest.mock('moment', () => {
  const moment = jest.requireActual('moment');
  moment.now = () => +new Date('2022-01-18T12:33:37.000Z');
  return moment;
});

Consultant answered 20/1, 2022 at 18:54 Comment(0)
G
1

I would like to offer some alternative approaches.

If you need to stub format() (which can be locale and timezone dependent!)

import moment from "moment";
...
jest.mock("moment");
...
const format = jest.fn(() => 'April 11, 2019')
moment.mockReturnValue({ format })

If you only need to stub moment():

import moment from "moment";
...
jest.mock("moment");
...
const now = "moment(\"2019-04-11T09:44:57.299\")";
moment.mockReturnValue(now);

Regarding the test for the isDateToday function above, I believe the simplest way would be not to mock moment at all

Guadalupeguadeloupe answered 11/4, 2019 at 7:47 Comment(2)
For the first example, I get TypeError: moment.mockReturnValue is not a functionLalittah
Is jest.mock("moment") at the same level as your import statements? Otherwise, you are welcome to see it in action in this projectGuadalupeguadeloupe
G
1

I'd like use Manual Mocks, so it can use in all tests.

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

Date.now = jest.fn(() => 1558281600000) // 2019-05-20 00:00:00.000+08:00

module.exports = moment
Gentle answered 23/5, 2019 at 2:28 Comment(0)
S
1

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', () => {...})

Subterrane answered 26/11, 2020 at 11:47 Comment(0)
B
1

Improving a bit the @pranava-s-balugari response

  1. It does noe affect new Date(something)
  2. The mocked date can be changed.
  3. It will work fot Date.now too
const DateOriginal = global.Date;

global.Date = class extends DateOriginal {
    constructor(params) {
        if (params) {
          super(params)
        } else if (global.Date.NOW === undefined) {
          super()
        } else {
          super(global.Date.NOW)
        }
    }
    static now () {
      return new Date().getTime();
    }
}

afterEach(() => {
  global.Date.NOW = undefined;
})

afterAll(() => {
  global.Date = DateOriginal;
});

describe('some test', () => {
  afterEach(() => NOW = undefined);

  it('some test', () => {
     Date.NOW = '1999-12-31T23:59:59' // or whatever parameter you could pass to new Date([param]) to get the date you want


     expect(new Date()).toEqual(new Date('1999-12-31T23:59:59'));
     expect(new Date('2000-01-01')).toEqual(new Date('2000-01-01'));
     expect(Date.now()).toBe(946681199000)

     Date.NOW = '2020-01-01'

     expect(new Date()).toEqual(new Date('2020-01-01'));
  })
})
Bobby answered 25/5, 2021 at 7:29 Comment(0)
I
1

The accepted answer works good -

Date.now = jest.fn().mockReturnValue(new Date('2021-08-29T18:16:19+00:00'));

But if we want to run unit tests in pipeline we have to make sure we are using the same time zone. To do that we have to mock timezone as well -

jest.config.js

process.env.TZ = 'GMT';

module.exports = {
 ...
};

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

Illassorted answered 9/9, 2021 at 18:50 Comment(0)
M
1

I was using an external library and to make it work I had to run this code on the setup stage:

Date.now = jest.fn(() => new Date(Date.UTC(2021, 2, 30)).valueOf());

I wrote this in my setupTests.ts file set in the setupFilesAfterEnv prop from jest.config.js:

module.exports = {
    setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'],
};
Malone answered 19/10, 2022 at 8:48 Comment(0)
Z
1

I ran into issues with most implementations.

While jest.setSystemTime(new Date(date)); is attractive, in practice, it was not overriding the date in many circumstances.

Just overriding Date.now is OK, but I also use new Date throughout the codebase, as does moment.

Finally, I felt that a package was simply overkill.

After much trial and error, taking some inspiration from the answers here, here is my solution, which inherits from Date, so it will have all it's properties, and will account for different types of parameters (such as multiple parameters, without which moment().startOf('day') wasn't working).

// default mock date, if you want one
export const setDateToReturnMockDate = (date) => {
  const mockDate = new Date(date);
  const _Date = Date;
  class MockDate extends _Date {
    // can accept an array, e.g. new Date(2023, 3, 2);
    constructor(...date) {
      return date.length ? new _Date(...date) : mockDate;
    }
  }
  MockDate.now = () => +mockDate;
  global.Date = MockDate;
};
Zimmermann answered 2/3, 2023 at 23:22 Comment(0)
A
1

Sometimes, using just

jest.useFakeTimers({
  now: 1673445238335,
});

leads to freezes in async/await functions. To avoid freezes, I don't mock some APIs:

      jest.useFakeTimers({
        doNotFake: [
          "setImmediate",
          "clearImmediate",
          "setTimeout",
          "clearTimeout",
          "setInterval",
          "clearInterval",
          "nextTick",
          "queueMicrotask",
        ],
        now: new Date(TEST_DATE),
      });
Arjun answered 5/9, 2023 at 19:13 Comment(0)
G
0

Goal is to mock new Date() with a fixed date wherever it's used during the component rendering for test purposes. Using libraries will be an overhead if the only thing you want is to mock new Date() fn.

Idea is to store the global date to a temp variable, mock the global date and then after usage reassign temp to global date.

export const stubbifyDate = (mockedDate: Date) => {
    /**
     * Set Date to a new Variable
     */
    const MockedRealDate = global.Date;

    /**
     *  Mock Real date with the date passed from the test
     */
    (global.Date as any) = class extends MockedRealDate {
        constructor() {
            super()
            return new MockedRealDate(mockedDate)
        }
    }

    /**
     * Reset global.Date to original Date (MockedRealDate) after every test
     */
    afterEach(() => {
        global.Date = MockedRealDate
    })
}

Usage in your test would be like

import { stubbyifyDate } from './AboveMethodImplementedFile'

describe('<YourComponent />', () => {
    it('renders and matches snapshot', () => {
        const date = new Date('2019-02-18')
        stubbifyDate(date)

        const component = renderer.create(
            <YourComponent data={}/>
        );
        const tree = component.toJSON();
        expect(tree).toMatchSnapshot();
    });
});


Gobetween answered 18/9, 2019 at 4:59 Comment(2)
Explain your Answer as well. putting code only is not the good approachLycia
Thanks for the suggestion. Updated with comments.Gobetween
S
0

I just wanted to chime in here since no answer addressed the issue if you want to mock the Date object in only a specific suite.

You can mock it using the setup and teardown methods for each suite, jest docs

/**
 * Mocking Date for this test suite
 */
const globalDate = Date;

beforeAll(() => {
  // Mocked Date: 2020-01-08
  Date.now = jest.fn(() => new Date(Date.UTC(2020, 0, 8)).valueOf());
});

afterAll(() => {
  global.Date = globalDate;
});

Hope this helps!

Shontashoo answered 8/1, 2020 at 21:53 Comment(1)
OMG why does Date.UTC use 0 based months? I thought I was going crazy, by being a month off the whole time. new Date(Date.UTC(2020, 0, 8)).valueOf() --> January 8th 2020 new Date(Date.UTC(2020, 1, 8)).valueOf() --> February 8th 2020 and then only for months, not days or years developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Randee
E
0

You can use date-faker. Lets you change the current date relatively:

import { dateFaker } from 'date-faker';
// or require if you wish: var { dateFaker } = require('date-faker');

// make current date to be tomorrow
dateFaker.add(1, 'day'); // 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond'.

// change using many units
dateFaker.add({ year: 1, month: -2, day: 3 });

// set specific date, type: Date or string
dateFaker.set('2019/01/24');

// reset
dateFaker.reset();
Enclose answered 17/3, 2020 at 7:51 Comment(0)
C
0

Best way I have found is just to override the prototype with whatever function you are using.

Date.prototype.getTimezoneOffset = function () {
   return 456;
};

Date.prototype.getTime = function () {
      return 123456;
};
Cordero answered 7/5, 2021 at 15:48 Comment(0)
G
0

The following test stubs Date to return a constant during the test lifecycle.

If you have use new Date() in your project then you could mock it in your test file something like this:

  beforeEach(async () => {
    let time_now = Date.now();
    const _GLOBAL: any = global;
    _GLOBAL.Date = class {
      public static now() {
        return time_now;
      }
    };
}

Now wherever you will use new Date() in your test file, It will produce the same timestamp.

Note: you could replace beforeEach with beforeAll. And _GLOBAL is just a proxy variable to satisfy typescript.

The complete code I tried:

let time_now;
const realDate = Date;

describe("Stubbed Date", () => {
  beforeAll(() => {
    timeNow = Date.now();
    const _GLOBAL: any = global;
    _GLOBAL.Date = class {
      public static now() {
        return time_now;
      }

      constructor() {
        return time_now;
      }

      public valueOf() {
        return time_now;
      }
    };
  });

  afterAll(() => {
    global.Date = realDate;
  });

  it("should give same timestamp", () => {
    const date1 = Date.now();
    const date2 = new Date();
    expect(date1).toEqual(date2);
    expect(date2).toEqual(time_now);
  });
});

It worked for me.

Gradation answered 4/8, 2021 at 9:16 Comment(0)
N
0

I recommend sinonjs/fake-timers. It's very similar to the fake timer provided by jest, but much more user-friendly.

import FakeTimers from '@sinonjs/fake-timers';

const clock = FakeTimers.install()
clock.setSystemTime(new Date('2022-01-01'));

console.log(new Date()) // 2020-01-01T00:00:00.000Z
Nates answered 24/9, 2022 at 4:33 Comment(0)
B
0
jest.useFakeTimers('modern').setSystemTime(new Date('2023-03-01'))
Bohaty answered 6/4, 2023 at 23:5 Comment(0)
V
0
jest.useFakeTimers({ now: Number(new Date()) })

Jest docs: https://jestjs.io/docs/timer-mocks

Velocity answered 23/6, 2023 at 10:7 Comment(0)
V
0

Using Jest fake timers may add additional complexity when working with async code: jest.useFakeTimers overrides the global setTimeout which is used in RTL waitFor, waitForNextUpdate and similar. Adding a jest spy could solve this:

import jest from 'jest';

jest.spyOn(Date, 'now');

const now = Date.now(); // mocked `Date.now() value

If you want to use a specific mocked date:

const mockDateNow = 1689975660000
jest.spyOn(Date, 'now').mockReturnedValue(mockDateNow);
Valence answered 21/7, 2023 at 21:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.