jest typescript - Mock Date constructor
Asked Answered
C

9

28

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?

Cockaigne answered 29/3, 2020 at 8:59 Comment(2)
found a solution?Remains
Nope. I use a workaround: I only use new Date(Date.now()) in my code and never new Date(). This way I can mock Date.now().Cockaigne
R
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!

Remains answered 25/6, 2020 at 17:2 Comment(5)
You can avoid the //@ts-ignore by appending to your class declaration as unknown as typeof Date.Portillo
This is not recommended by Jest. You should be using Jest Spies for any global classes.Dubbing
Good idea! But when calling data.getFullYear() I get this is not a Date object.Gayden
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;Eventual
@Eventual Thank you, I was just going to ask what is the best way to reset.Melchizedek
P
10

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();
Polyphone answered 27/4, 2021 at 7:4 Comment(1)
Finally! This worked for meSiana
S
8

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.

Strawboard answered 29/3, 2020 at 18:13 Comment(2)
Thanks. I think the library works because it's in JS, not TypeScript. I suspect that the problem is with the translation from TS to JS. I'm looking for a TS solution.Cockaigne
@EllaSharakanski this lib is now in TS.Infantryman
W
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
Weaverbird answered 7/9, 2020 at 17:40 Comment(3)
That doesn't help with mockingCockaigne
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)Rasping
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 functionCapel
V
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);
Vulcanism answered 3/9, 2021 at 19:30 Comment(0)
C
3

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)
Councilwoman answered 22/1, 2023 at 12:57 Comment(0)
L
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...
Lulu answered 1/2, 2023 at 16:16 Comment(0)
F
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);
  });
});
Fabrienne answered 29/3, 2023 at 4:30 Comment(0)
S
0

To use setSystemTime as suggested, useFakeTimers is required first. In order to not break timeouts and such, advanceTimers: true can be passed.

const mockDate = new Date('2024-02-03T12:05:33.243Z');
jest.useFakeTimers({ advanceTimers: true });
jest.setSystemTime(mockDate);
console.log(new Date());  // prints "2024-02-03T12:05:33.243Z"

The solution using mockImplementation doesn't work for me, because it breaks Date.now().

Sextet answered 5/3 at 13:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.