58

I'm unable to mock moment() or moment().format functions. I have states where, currentDateMoment and currentDateFormatted are getting set as below.

currentDateMoment: moment() //2019-04-23T17:45:26.339Z
currentDateFormatted: moment().format('MM-DD-YYYY').valueOf() //"04-23-2019"

Trying to mock both moment() and moment().format in my snapshot tests to return a particular date, but was unable to. Tried below.

jest.mock('moment', () => () => '2018–01–30T12:34:56+00:00');

jest.mock('moment', () => ({
  constructor: () => '2018–01–30T12:34:56+00:00'
})); 

jest.mock('moment', () => () => ({ format: () => '01–30-2018' }));
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Zin Yackvaa
  • 835
  • 1
  • 7
  • 15
  • This seems like a strange thing to be mocking, why do you need to inject a particular date to perform your testing? – Jake Holzinger Apr 24 '19 at 21:31
  • 2
    I have a snapshot test and need to mock moment() to return a certain date to not have it fail the next day... – Zin Yackvaa Apr 24 '19 at 21:35
  • 2
    Possible duplicate of [How to mock moment.utc() for unit tests?](https://stackoverflow.com/questions/46891897/how-to-mock-moment-utc-for-unit-tests) – VincenzoC Apr 24 '19 at 22:30
  • I am also having the same issue. Any update? – AJ007 Jul 04 '19 at 07:21
  • Similar issue in the code base. Do we have a fix for this yet? – koustubh Sep 19 '19 at 12:29
  • Related: https://stackoverflow.com/a/65130857/3001761. Don't mock things (`moment`, `Date`) you don't own, and treat time as a *dependency*. – jonrsharpe Feb 24 '21 at 16:54
  • Do you have any ideas for this issue: https://stackoverflow.com/questions/68209029/jest-mock-moment-to-return-specific-date – Tan Dat Jul 01 '21 at 11:10

9 Answers9

65

The easiest way to mock moment() and any function that it uses (i.e. .day(), .format()) is to change the Date that moment() uses under the hood

Add the snippet below inside your test file

Date.now = jest.fn(() => new Date("2020-05-13T12:33:37.000Z"));

This makes it so anytime moment() is called in your tests, moment() thinks that today is Wednesday, May 13th 2020

giantriant
  • 783
  • 5
  • 4
  • 14
    Yes, I like this too, it's simple to setup. I personally like using it this way: ```Date.now = jest.fn().mockReturnValue(new Date('2020-05-13T12:33:37.000Z'));``` – mc. May 21 '20 at 19:07
  • 6
    I successfully used `jest.spyOn(Date, 'now').mockImplementation(...)` – Chris Nov 25 '20 at 15:15
  • it does not work on my case, moment() return null after I mock like this https://stackoverflow.com/questions/68209029/jest-mock-moment-to-return-specific-date – Tan Dat Jul 01 '21 at 11:12
  • I use this on react-native `const mockNowDate = (date: string) => (global.Date.now = jest.fn(() => new Date(date).getTime()));` and rollback to the real one afterAll test `afterAll(() => { global.Date = RealDate;});` – Roni Castro Aug 31 '21 at 00:12
  • 5
    Neat, in Typescript remember to add: `Date.now = jest.fn(() => new Date('2020-11-01T12:33:37.000Z').getTime());` – k-wasilewski Dec 02 '21 at 14:23
  • If moment will change implementation in some way this will break all the tests that relay on this – Oz Ben-David Feb 27 '22 at 09:41
34

You can mock Moment to return a specific date, then format don't have to be mocked.

jest.mock('moment', () => {
  return () => jest.requireActual('moment')('2020-01-01T00:00:00.000Z');
});

By doing so, any call to Moment() will always return a moment object with date set to 2020-01-01 00:00:00

Here is an example with a function that return the date of tomorrow and the test for this function.

const moment = require('moment');
const tomorrow = () => {
  const now = moment();
  return now.add(1, 'days');
};

describe('tomorrow', () => {
  it('should return the next day in a specific format', () => {
    const date = tomorrow().format('YYYY-MM-DD');
    expect(date).toEqual('2020-01-02');
  });
});
felixyadomi
  • 3,197
  • 1
  • 22
  • 28
  • do you have any ideas about this? https://stackoverflow.com/questions/68209029/jest-mock-moment-to-return-specific-date – Tan Dat Jul 01 '21 at 11:11
8

Here is the solution:

index.ts:

import moment from 'moment';

export function main() {
  return {
    currentDateMoment: moment().format(),
    currentDateFormatted: moment()
      .format('MM-DD-YYYY')
      .valueOf()
  };
}

index.spec.ts:

import { main } from './';
import moment from 'moment';

jest.mock('moment', () => {
  const mMoment = {
    format: jest.fn().mockReturnThis(),
    valueOf: jest.fn()
  };
  return jest.fn(() => mMoment);
});

describe('main', () => {
  test('should mock moment() and moment().format() correctly ', () => {
    (moment().format as jest.MockedFunction<any>)
      .mockReturnValueOnce('2018–01–30T12:34:56+00:00')
      .mockReturnValueOnce('01–30-2018');
    expect(jest.isMockFunction(moment)).toBeTruthy();
    expect(jest.isMockFunction(moment().format)).toBeTruthy();
    const actualValue = main();
    expect(actualValue).toEqual({ currentDateMoment: '2018–01–30T12:34:56+00:00', currentDateFormatted: '01–30-2018' });
  });
});

Unit test result with 100% coverage:

 PASS  src/stackoverflow/55838798/index.spec.ts
  main
    ✓ should mock moment() and moment().format() correctly  (7ms)

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |      100 |      100 |      100 |      100 |                   |
 index.ts |      100 |      100 |      100 |      100 |                   |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.795s, estimated 8s

Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/55838798

Lin Du
  • 88,126
  • 95
  • 281
  • 483
  • Did not work for me unfortunately in my js test case. I am importing moment as-> import * as moment from 'moment'; – Peter Jan 29 '20 at 09:24
  • This is a good example of why you _shouldn't_ mock moment, this is _so_ coupled to the implementation. – jonrsharpe Apr 29 '21 at 11:54
5

mockdate works for me

import mockDate from "mockdate";


test('Should add some', () => {
    mockDate.set(new Date('2/20/2020'));

    const action = addSome();

    expect(action).toEqual({
        createdAt: moment()
    });

    mockDate.reset();
})
Pavel Zorin
  • 331
  • 6
  • 17
3

TL;DR Found testing moment with jest and it really helped me with my solution.

Context I needed to mock format with the moment-timezone so here is my solution and ran into issues where moment, format or other methods within the moment function were not defined.

Solution

// mock the module using automock from jest
jest.mock('moment-timezone', () => {
    // mocking a module requires the function definitions, to only partially mock, use requireActual 

    const moment = jest.requireActual('moment-timezone');
    // create a momentInstance
    const momentInstance = moment();

    // Override the format function with some mockedTime
    jest.spyOn(momentInstance, 'format').mockImplementation(() => mockedTime);

    function fakeMoment() {
        return momentInstance;
    }

    Object.assign(fakeMoment, moment);

    return fakeMoment;
});
16egong
  • 31
  • 1
2

The other answers tell you how to mock moment, but you don't need to mock moment to test that code. You probably shouldn't, in fact; it's a complicated third party interface you don't own and mocking it couples your tests to it, you end up testing implementation rather than behaviour.

Rather than calling moment() without any arguments, call it with the current date (per https://momentjscom.readthedocs.io/en/latest/moment/01-parsing/01-now/ moment() is basically moment(new Date())). Then that current date comes from a function you do own:

const { getCurrentDate } = require('path/to/utils');

export const createSomething =() => ({
  currentDateMoment: moment(getCurrentDate()),
  currentDateFormatted: moment(getCurrentDate()).format('MM-DD-YYYY'),
});

so you can trivially mock that out in the tests:

const { createSomething } = require('path/to/impl');
const { getCurrentDate } = require('path/to/utils');
jest.mock('path/to/utils');

it('formats the data correctly', () => {
  getCurrentDate.mockReturnValue(new Date(2021, 3, 29));

  const { currentDateFormatted } = createSomething();
  expect(currentDateFormatted).toEqual('2021-04-29');
});

Now the test doesn't refer to moment, which has become an implementation detail, at all. If there's a breaking future change to the moment API you'll find out about it, because your tests will fail; they'd misleadingly pass if it was mocked. If you want to switch to a different library you can do so, confident that the tests mean the behaviour is still correct (here's an example doing the same thing with DayJS).

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
1

To Mock Moment().format(),startOf() , isValid() or isAfter() etc. Can refer below example.

jest.mock('moment', () => {
    
    const momentParams = {
        format: jest.fn(() => '10/04/2020'),
        startOf: jest.fn().mockReturnThis(),
        isAfter: jest.fn().mockReturnValue(true),
        isValid: jest.fn().mockReturnValue(true)
    };

   const fn = jest.fn(newMoment => {
        momentParams.format = jest.fn(() => newMoment);
        return momentParams;
   });

   return fn;
});

And last you can write a test case like this. eg.

 test('should returned mocked value for isAfter()', async () => {
    jest.spyOn(moment(), 'isAfter').mockReturnValue(false);
    const response = moment().isAfter();
    expect(response).toBe(false)
})
Maheshvirus
  • 6,749
  • 2
  • 38
  • 40
0

I was still getting some errors because I was using moment-timezone, too. So, here's what I did to fix that:

let diffMins = updateThreshold + 1;
jest.mock('moment', () => {
  const mMoment = {
    diff: jest.fn(() => diffMins),
  };
  const fn = jest.fn(() => mMoment);
  fn.version = '2.24';
  fn.tz = jest.fn();
  fn.fn = jest.fn();
  return fn;
});
Rusty Divine
  • 3,462
  • 1
  • 21
  • 15
0

I have followed above post and have some errors because only mock some functions instead of moment object time.

Changed a little bit as below

jest.mock('moment-timezone', () => {
   const moment = require('moment');
   // Mock to the date you want
   return moment.utc('2023-01-01 08:30:40')
}

Follow this config, the test function worked as expectation and don't have the error: unix method is null, format method is undefined etc

Luan Nguyen
  • 186
  • 1
  • 5