26

I'm trying to mock new Date() to return a specific date. The following code:

const now = new Date()
jest.spyOn(global, 'Date').mockImplementation(() => now)

gives a compilation error: Argument of type '() => Date' is not assignable to parameter of type '() => string'. Type 'Date' is not assignable to type 'string'.

I think the reason is that jest thinks I'm trying to mock Date() instead of new Date(). Indeed, Date() returns a string. How can I solve this issue?

skyboyer
  • 22,209
  • 7
  • 57
  • 64
Ella Sharakanski
  • 2,683
  • 3
  • 27
  • 47

8 Answers8

12

well I tried this solution, and it worked..

class MockDate extends Date {
    constructor() {
        super("2020-05-14T11:01:58.135Z"); // add whatever date you'll expect to get
    }
}

and in the beforeEach of that test, I added:

// @ts-ignore
global.Date = MockDate;

this way whenever I called a function that had new Date() inside, it would return the date I added in the constructor of the MockDate class above!

Riham Nour
  • 387
  • 4
  • 10
  • 1
    You can avoid the `//@ts-ignore` by appending to your class declaration `as unknown as typeof Date`. – Geoffrey Booth Feb 27 '21 at 01:51
  • 1
    This is not recommended by Jest. You should be using Jest Spies for any global classes. – Borduhh Mar 17 '21 at 19:28
  • 1
    Good idea! But when calling `data.getFullYear()` I get `this is not a Date object.` – Chaoste Apr 01 '21 at 07:43
  • it's a good practive to also restore back the global date. const DateReal = global.Date; global.Date = MockDate; your test case here ...; global.Date = MockDate as any; – Bogdan Mar 25 '22 at 19:28
  • 1
    @Bogdan Thank you, I was just going to ask what is the best way to reset. – Jimmy Vo Jul 25 '22 at 21:34
8

Daryn's inference comment on Arron's answer works great with no extra packages.

const mockDate = new Date();
jest.spyOn(global, "Date").mockImplementation(() => (mockDate as unknown) as string);
const myDate = new Date();
Malt
  • 151
  • 1
  • 3
7

A workaround is to use the mockdate library, which can be used to change when "now" is.

const MockDate = require('mockdate');

test('Mock Date to change when "now" is', () => {
  console.log('Normal:   ', new Date().getTime());

  MockDate.set(new Date(1466424490000));

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

  MockDate.reset();

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

And the test result is like:

$ npm run test
> jest

 PASS  src/test.ts
  ✓ Mock Date to change when "now" is (8ms)

  console.log src/test.ts:4
    Normal:    1585505136315

  console.log src/test.ts:8
    Mocked:    1466424490000

  console.log src/test.ts:12
    Restored:  1585505136322

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.804s

See the reference project on GitHub.

Yuci
  • 27,235
  • 10
  • 114
  • 113
3

Just tell the compiler what it wants to hear: say its a string with cast to unknown followed by the cast to string:

const now = new Date() as unknown as string
  • 2
    That doesn't help with mocking – Ella Sharakanski Sep 08 '20 at 14:58
  • 4
    From Aaron Goldman's comment you could infer he meant `const mockDate = new Date(1466424490000) const spy = jest.spyOn(global, 'Date') .mockImplementation(() => mockDate as unknown as string)` – Daryn Oct 19 '20 at 07:33
  • 2
    But new Date() now returns a string and any calls on the new Date(), e.g. new Date().getTime() will not work because "string".getTime() isn't a function – PulpDood Jan 25 '22 at 10:38
3

I have the same issue when mocking a new Date() using jest typescript when try to mocking Date inside a function :

export const checkValidTime(): boolean => { 
  const now = new Date();
  const maxAllowTime = new Date('2020-02-02 09:00:00');

  console.log(`currentTime: ${now.toLocaleString()}`);
  console.log(`maxAllowTime: ${maxAllowTime.toLocaleString()}`);

  return now > maxAllowTime;
}

Mocking solution in unit test:

const mockDate = new Date('2021-09-03 08:00:10');
jest
    .spyOn(global, 'Date')
    .mockImplementationOnce(() => (mockDate as unknown) as string);
const valid = checkDateTime();
expect(valid).toEqual(true);
anjarwidi83
  • 446
  • 3
  • 8
2

There is a Jest built-in support for Date mocking: jest.setSystemTime

It is available since Jest 26.x

It will mock the new Date() and Date.now() results.

Usage example:

const mockDate = new Date('2023-01-22T00:00:00.000Z')
jest.setSystemTime(mockDate)
v.karbovnichy
  • 3,183
  • 2
  • 36
  • 47
0

This works for me under TypeScript unit testing with jest...

const fakeNow = new Date;
jest.spyOn(global, 'Date').mockReturnValue(fakeNow);

// Anything that checks date can compare against fakeNow...
Jason
  • 941
  • 1
  • 10
  • 19
0

Update on 2023 using Typescript

// utils.ts

import { format } from 'date-fns';

export const printTimestamp = (datetime?: string | undefined) => {
  let d = new Date();
  if (datetime !== undefined) {
    d = new Date(datetime);
  }
  const date = format(d, 'dd MMMM, yyyy');
  const time = format(d, 'hh:mm');
  return `${date} at ${time}`;
};

// utils.spec.ts
import { printTimestamp } from './util';

describe('timestamp', () => {
  it('handle empty timestamp', () => {
    jest.useFakeTimers();
    jest.setSystemTime(new Date('2022-03-19 12:00:00'));
    const expected = '19 March, 2022 at 12:00';
    expect(printTimestamp()).toEqual(expected);
  });
  it('handle inputted timestamp', () => {
    const input = '2022-03-25 12:00:00';
    const expected = '25 March, 2022 at 12:00';
    expect(printTimestamp(input)).toEqual(expected);
  });
});
ejabu
  • 2,998
  • 23
  • 31