Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mocking current time for Date #2234

Closed
calebmer opened this issue Dec 6, 2016 · 74 comments
Closed

Mocking current time for Date #2234

calebmer opened this issue Dec 6, 2016 · 74 comments

Comments

@calebmer
Copy link
Contributor

calebmer commented Dec 6, 2016

EDIT by @SimenB 25-05-2020: See updated answer: #2234 (comment)

Is there a way to mock the current date? So that new Date() or Date.now() returns a mocked time instead of the current time?

@cpojer
Copy link
Member

cpojer commented Dec 6, 2016

Date.now = jest.fn or global.Date = jest.fn().

@cpojer cpojer closed this as completed Dec 6, 2016
@kristojorg
Copy link

For anyone else out there running in to errors with this, I had some issues because global Date object has properties other than constructor. I did the following:

const DATE_TO_USE = new Date('2016');
const _Date = Date;
global.Date = jest.fn(() => DATE_TO_USE);
global.Date.UTC = _Date.UTC;
global.Date.parse = _Date.parse;
global.Date.now = _Date.now;

@callemo
Copy link

callemo commented Jun 13, 2017

If you do not need to assert on how the constructor is being called then extending could be enough:

const constantDate = new Date('2017-06-13T04:41:20')

/*eslint no-global-assign:off*/
Date = class extends Date {
  constructor() {
    return constantDate
  }
}

@wilsonsilva
Copy link

This works pretty well for Date.now().

const now = Date.now()
Date.now = jest.genMockFunction().mockReturnValue(now)

@vcarel
Copy link

vcarel commented Aug 25, 2017

Whenever you mock date, don't forget to put back the real version.
Following @callemo comment, you may use the following snippet:

describe('getTimestamp', () => {
  const RealDate = Date

  function mockDate (isoDate) {
    global.Date = class extends RealDate {
      constructor () {
        return new RealDate(isoDate)
      }
    }
  }

  afterEach(() => {
    global.Date = RealDate
  })

  it('should return timestamp', () => {
    mockDate('2017-11-25T12:34:56z')
    expect(getTimestamp()).toEqual('20171125123456')
  })
})

@niieani
Copy link
Contributor

niieani commented Sep 28, 2017

Arguably, Date should also be moved forward when running jest.runTimersToTime() and other time mocking functions. I was bitten by the same issue, since part of my code depended on time, and part on timeouts. Mocking them both at the same time -- i.e. running mocked timers AND switching the Date.now and Performance.now mocks is not the best experience.

A solution for a "unified" timers mock was to use lolex instead of jest builtins, like this:

import lolex from 'lolex'

describe('tests', () => {
  let clock
  beforeEach(() => {clock = lolex.install()})
  afterEach(() => {clock = clock.uninstall()})

  test('garbage collects after keep alive', () => {
    // ...
    clock.tick(500)
    // ...
  })
})

But it would be great to have this feature builtin.

@joaoreynolds
Copy link

Old issue, but mockdate makes this easy: https://www.npmjs.com/package/mockdate

@drpicox
Copy link

drpicox commented Oct 31, 2017

https://jasmine.github.io/2.2/introduction?spec=jasmine.any#section-Mocking_the_Date

   describe("Mocking the Date object", function(){
     it("mocks the Date object and sets it to a given time", function() {
       var baseTime = new Date(2013, 9, 23);
       jasmine.clock().mockDate(baseTime);
       jasmine.clock().tick(50);
       expect(new Date().getTime()).toEqual(baseTime.getTime() + 50);
     });
   });

@niieani
Copy link
Contributor

niieani commented Oct 31, 2017

@drpicox that's a nice solution, however AFAIK it doesn't mock performance.now(), which is a useful, forward-only clock (i.e. won't be set back by user changing his system's DateTime).

@drpicox
Copy link

drpicox commented Oct 31, 2017

In fact it does not work in Jest. Jest uses Jasmine v1.5.2-lite, so it has no clock. I am using lolex.

Date.now() is good enough for most applications, permofrmance.now() it is not present in node yet —cannot be used in SSR for example—, so it does not seems to be a big deal.

Of course, lolex is not integrated with jest.

@niieani
Copy link
Contributor

niieani commented Oct 31, 2017

@drpicox ah, good to know it doesn't work then.
performance.now() is present in node, since v8.5.0 I believe. You can import performance from the builtin module 'perf_hooks'.

Still, given the current situation and the number of votes/comments this is getting, I would like to ping @cpojer to consider re-opening this issue.

@SimenB
Copy link
Member

SimenB commented Oct 31, 2017

FWIW I'd love to integrate lolex - it's the only library I use where I think Jest is missing a battery

@cafesanu
Copy link

cafesanu commented Jan 5, 2018

@cpojer Any way we can reopen this. As of now, there is not really a way to mock a date AND simulate the pass of time in an easy way.

jasmine has a clock class where you can mock a date AND advance time, via:

jasmine.clock().install(); //in a beforeEach
jasmine.clock().uninstall(); // in a AfterEach
jasmine.clock().mockDate(new Date('1984/12/15'));

// and very important... the pass of time!
jasmine.clock().tick(100);

Would love to have similar functionality native. https://github.com/jasmine/jasmine/blob/master/src/core/Clock.js

@SimenB
Copy link
Member

SimenB commented Jan 5, 2018

We will attempt to migrate to Lolex, which supports your use case. See #5165

@omegdadi-sc
Copy link

omegdadi-sc commented Mar 2, 2018

In case you need to mock Date outside of a test environment. I needed to take predictable image snapshots of UI where a date is displayed.

This worked for me:
https://github.com/schickling/timemachine

timemachine.config({
  dateString: 'December 25, 1991 13:12:59'
});
console.log(new Date()); // December 25, 1991 13:12:59

@gre
Copy link

gre commented Mar 21, 2018

@omegdadisc 's suggestion is the best I think. Mocking Date to always return the same date (as it was suggested in the first answers) would mess up with things like new Date(2018, 2, 3) so was not a valid option to me.

Note that you also need to precise the timezone for it to fully work everywhere, e.g. on Travis and produce same result.

timemachine.config({
  dateString: 'December 25, 1991 13:12:59 GMT'
});

@nilobarp
Copy link

nilobarp commented Apr 4, 2018

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

let timeNow;
const realDate = Date;

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

      constructor() {
        return timeNow;
      }

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

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

  it("provides constant timestamps", () => {
    const ts1 = Date.now();
    const ts2 = +new Date();
    expect(ts1).toEqual(ts2);
    expect(ts2).toEqual(timeNow);
  });
});

_GLOBAL is just a proxy variable to satisfy typescript.

@the-mold
Copy link

I needed to mock Date.now()

setting the below line in config or before tests worked for me:

jest.spyOn(Date, 'now').mockImplementation(() => 1479427200000)

@ivarne
Copy link

ivarne commented Apr 27, 2018

I liked @vcarel's approach, but in my case the Date constructor was used with arguments in some cases, so I needed to modify it to accept other dates. I also added Date.now()

describe('getTimestamp', () => {
  const RealDate = Date

  function mockDate (isoDate) {
    global.Date = class extends RealDate {
      constructor(...theArgs) {
        if (theArgs.length) {
          return new RealDate(...theArgs);
        }
        return new RealDate(isoDate);
      }
    
      static now() {
        return new RealDate(isoDate).getTime();
      }
    }
  }

  afterEach(() => {
    global.Date = RealDate
  })

  it('should return timestamp', () => {
    mockDate('2017-11-25T12:34:56z')
    expect(getTimestamp()).toEqual('20171125123456')
  })
})

@adrienharnay
Copy link

I'm using this, which I'm happy with: https://github.com/boblauer/MockDate

@iwarner
Copy link

iwarner commented May 21, 2018

I have done this

describe('Test', () => {
  const constantDate = new Date('2018-01-01T12:00:00')

  beforeAll(() => {
    global.Date = class extends Date {
      constructor () {
        super()
        return constantDate
      }
    }
  })

@ghost
Copy link

ghost commented Jun 12, 2018

Just like to add slightly to @callemo's and @iwarner's answer.

It's probably less error prone to do something like as it returns a new date instance each time:

  const constantDate = new Date('2018-01-01T12:00:00')

  beforeAll(() => {
    global.Date = class extends Date {
      constructor () {
        super(constantDate.getTime())
      }
    }
  })

This allows functions that mutate date objects (e.g. setMinutes) to be used without mutating constantDate and thus altering the date that is returned from new Date e.g.

describe('Test', () => {
  const constantDate = new Date('2018-01-01T12:00:00')

  beforeAll(() => {
    global.Date = class extends Date {
      constructor () {
        super()
        return constantDate
      }
    }
  });

  it('it should not be possible to mutate the  original date but it is.', () => {
    const date1 = new Date();
    date1.setMinutes(5);
    const date2 = new Date();
    console.log(date2.getMinutes()); // Will print 5
  });
});

@samboylett
Copy link

This is what I'm using after reading all of the above:

let currentDate;

beforeAll(() => {
  currentDate = new Date();

  const RealDate = Date;
  global.Date = jest.fn(() => new RealDate(currentDate.toISOString()));
  Object.assign(Date, RealDate);
});

@aaronleesmith
Copy link

@samboylett Does this mess up the global date for tests in the future? Did you also reset it in an afterAll function?

@samboylett
Copy link

It won't affect tests in different files. For me everything in the current file needed the mocked date, but if you need to reset it between tests in the same file you should use a beforeEach and afterEach, and just set it back in the afterEach:

afterEach(() => {
  global.Date = RealDate;
});

@JuhQ
Copy link
Contributor

JuhQ commented Jul 12, 2018

@samboylett thanks! I managed to get my date tests working by using your example as a base.

const myDate = new Date(2018, 6, 11);

const RealDate = Date;

describe('testcase', () => {
  beforeEach(() => {
    global.Date = jest.fn(
      (...props) =>
        props.length
          ? new RealDate(...props)
          : new RealDate(myDate)
    );
    Object.assign(Date, RealDate);
  });

  afterEach(() => {
    global.Date = RealDate;
  });
});

swinton added a commit to swinton/linter-alex that referenced this issue Jul 18, 2018
@Diokuz
Copy link
Contributor

Diokuz commented Jul 1, 2019

Sinon works well for me https://sinonjs.org/releases/v1.17.7/fake-timers/

@dangerousdan
Copy link

Late to the party, but I think jest has all the functionality you need for this.

describe('how to mock date.now()', function() {
  beforeEach(() => {
    this.timestamp = 0
    global.Date.now = jest.fn().mockImplementation(() => this.timestamp)
  })

  afterEach(() => {
    jest.clearAllMocks()
  })

  it('can advance in time', () => {
    const then = Date.now()

    this.timestamp += 1000

    const now = Date.now()

    expect(now - then).toBe(1000)
  })

@cepm-nate
Copy link

cepm-nate commented Oct 3, 2019

I'm using vue-moment and jest, and have found the best way is to do something like this:

import { mount, createLocalVue } from '@vue/test-utils';
import TripList from 'components/trip-list.vue';

const localVue = createLocalVue();
localVue.use(require('vue-moment'));

describe('TripList', () => {

it('text shows current date', () => {
    const myDate = new Date(2019, 5, 5);

    const wrapper = mount(TripList, {
      localVue,
    });

    wrapper.vm.$moment.now = () => myDate;
    wrapper.vm.$forceUpdate();

    expect(wrapper.html()).toContain('Thu, Oct 3rd');
  });
})

@ayxos
Copy link

ayxos commented Dec 10, 2019

Using ES6

const fixedDate = new Date('2019-03-1');
const RealDate = Date;

beforeEach(() => { Date.now = () => fixedDate; });
afterEach(() => { global.Date = RealDate; });

@oleg-koval
Copy link

If you are using Date.now()

const dateSpy = jest.spyOn(Date, 'now');
dateSpy.mockReturnValue(TIMESTAMP);

@GoelBiju
Copy link

GoelBiju commented Jan 13, 2020

For anyone else out there running in to errors with this, I had some issues because global Date object has properties other than constructor. I did the following:

const DATE_TO_USE = new Date('2016');
const _Date = Date;
global.Date = jest.fn(() => DATE_TO_USE);
global.Date.UTC = _Date.UTC;
global.Date.parse = _Date.parse;
global.Date.now = _Date.now;

I wanted to mock the whole Date class and the method suggested by @kristojorg seemed ideal.

I am not sure if this is the appropriate way to do it, but this solution works fine in TypeScript by fixing the typing issue as mentioned by @nilobarp:

describe('Mock Date', () => {
  const realDateNow = Date.bind(global.Date);

  beforeAll(() => {
    // Fix the time to 2020-1-1 1hr:1min:1sec in order to match
    // snapshots for the DownloadConfirmDialog component.
    const fixedDate = new Date(2020, 0, 1, 1, 1, 1);
    const d = Date;

    // This will only mock any Date objects instantiated with new 
    // and not Date.now().
    const _global: NodeJS.Global = global;
    _global.Date = jest.fn(() => fixedDate);
    _global.Date.parse = d.parse;
    _global.Date.UTC = d.UTC;
    _global.Date.now = d.now;
  });

  it('shows mocked date', () => {
    // Shows 2020-01-01T01:01:01.000Z as the current Date
    // for an instantiated Date object.
    console.log(new Date().toISOString());
  });

  afterAll(() => {
    // Reverts to the current Date object.
    global.Date = realDateNow;
    console.log(new Date().toISOString());
  });
});

It is worth mentioning that this only works when you instantiate a new Date object and will not work for Date.now(). There is a comment regarding Date.now.

@warpdesign
Copy link

Inside a test block I'm doing something like...

      Date.now = () => 1;
      Date = null;
      global.Date = null;
      console.log(`Date.now()`, Date.now()); // Date.now() 1560239936091

But that override does nothing. I find it very confusing.

I am having the same problem: tested a whole bunch of code examples found in this issue and none worked for me: mock appears to be ignored.

How did you fix your problem?

We are also using Babel so this may be related too.

@GoelBiju
Copy link

GoelBiju commented Mar 11, 2020

Inside a test block I'm doing something like...

      Date.now = () => 1;
      Date = null;
      global.Date = null;
      console.log(`Date.now()`, Date.now()); // Date.now() 1560239936091

But that override does nothing. I find it very confusing.

I am having the same problem: tested a whole bunch of code examples found in this issue and none worked for me: mock appears to be ignored.

How did you fix your problem?

We are also using Babel so this may be related too.

@warpdesign this solution seems to work for me with Jest in TypeScript.

describe('Mock Date.now', () => {
  // Bind to the original Date so we can set it back after all tests are finished.
  const realDateNow = Date.now.bind(global.Date);

  beforeAll(() => {
    // Return 1 millisecond when calling Date.now() in tests.
    const dateNowStub = jest.fn(() => 1);
    global.Date.now = dateNowStub;
  });

  it('shows mocked date', () => {
    console.log(Date.now());  // Returns 1.
  });

  afterAll(() => {
    // Set back to the original Date object.
    global.Date.now = realDateNow;
    console.log(Date.now()); // Returns current time in milliseconds.
  });
});

From this article on mocking the current Date in Jest.

@Ekzer
Copy link

Ekzer commented Apr 4, 2020

Ultimate Solution: jest-date-mock.

  • install

In your package.json under the jest, create a setupFiles array and add jest-date-mock to the array.

{
  "jest": {
    "setupFiles": ["./__setups__/other.js", "jest-date-mock"]
  }
}
  • usage

Give you simple API to control the current timestamp for you test cases.

import { advanceBy, advanceTo, clear } 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
});

I think: All the other solutions are not systematic, or temporary.

Thank you it worked !!

@shivkumarganesh
Copy link

I followed Hugo's tutorial and ended up doing:

jest
  // @ts-ignore
  .spyOn(global.Date, 'constructor')
  .mockImplementationOnce(() => new Date('2019-06-19T00:07:19.309Z'))

So, new Date() will always return new Date('2019-06-19T00:07:19.309Z')

What crap is this man. U say Hugo's tutorial and Hugo says yours.... please don't mislead us.

@SimenB
Copy link
Member

SimenB commented May 25, 2020

Jest 26 supports mocking Date using modern fake timers: https://jestjs.io/blog/2020/05/05/jest-26#new-fake-timers

@nikksan
Copy link

nikksan commented Jun 23, 2020

If you are stuck with trying to mock the Date constructor in TS, try this:

const mockDate = new Date('Tue, 23 Jun 2020 14:34:56');
const RealDate = Date;
(global as any).Date = class extends RealDate {
  constructor() {
    super();
    return mockDate;
  }
};

// test some date related functionality

global.Date = RealDate;

tryggvigy added a commit to tryggvigy/use-debounce that referenced this issue Aug 31, 2020
this fixes jestjs/jest#2234
and allows us to properly mock the combination of
setTimeout and Date.now()
@gregveres
Copy link

Jest 26 supports mocking Date using modern fake timers: https://jestjs.io/blog/2020/05/05/jest-26#new-fake-timers

@SimenB That's great and I just updated to v26 but can't figure out how we use jest to mock the date. The jest docs only talk about how to mock timers so you can test setTimeOut, but how do you do it to mock "new Date()"?

@seansullivan
Copy link

seansullivan commented Nov 19, 2020

@gregveres Here is an example test suite that sets the system time (Date methods) to a fixed date and handles advancing that time.

const FIXED_SYSTEM_TIME = '2020-11-18T00:00:00Z';

describe('Set Fixed Date', () => {
    beforeEach(() => {
        jest.useFakeTimers('modern');
        jest.setSystemTime(Date.parse(FIXED_SYSTEM_TIME));
    });

    afterEach(() => {
        jest.useRealTimers();
    });
    
    it('Should reflect fixed date', () => {
        expect(new Date().toISOString()).toEqual(FIXED_SYSTEM_TIME);

        const MS_TO_ADVANCE = 5000;

        jest.advanceTimersByTime(MS_TO_ADVANCE);

        expect(new Date().toISOString()).toEqual(new Date(Date.parse(FIXED_SYSTEM_TIME) + MS_TO_ADVANCE).toISOString());
    });
});

Update: Included afterEach() per @alexdanilowicz's comment below.

@alexdanilowicz
Copy link

Update from Nov. 2020

Bolding that ^ as this thread is growing old and linked to a popular stackoverflow post

@seansullivan Awesome! FYI, correct me if I'm wrong, but I think it would be nice to add an afterEach (or afterAll) to your specs to reset the date back to reality. Just to be safe.

describe('Test', () => {
  // to avoid race conditions between expected and actual date values
  beforeAll(() => {
    jest.useFakeTimers('modern'); // tell Jest to use a different timer implementation.
    jest.setSystemTime(new Date('20 Aug 2020 00:12:00 GMT').getTime())
  });

  afterAll(() => {
    // Back to reality...
    jest.useRealTimers();
  });

@seansullivan
Copy link

@alexdanilowicz Perfect. Yes, I neglected to include the cleanup in my example. I will update with the afterEach() to ensure sanity is maintained for anybody who finds this thread. Thanks!

@doantrungnghia
Copy link

For anyone else out there running in to errors with this, I had some issues because global Date object has properties other than constructor. I did the following:

const DATE_TO_USE = new Date('2016');
const _Date = Date;
global.Date = jest.fn(() => DATE_TO_USE);
global.Date.UTC = _Date.UTC;
global.Date.parse = _Date.parse;
global.Date.now = _Date.now;

It's work to me. Thank you !

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 10, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests