Jestでモック日付を設定するにはどうすればよいですか?


111

Moment.jsを使用して、Reactコンポーネントのヘルパーファイルでほとんどの日付ロジックを実行していますが、Jest a laで日付をモックする方法を理解できていませんsinon.useFakeTimers()

JestのドキュメントではsetTimeoutsetIntervalなどのタイマー関数についてのみ説明していますが、日付を設定して、日付関数が意図したとおりに機能することを確認することはできません。

これが私のJSファイルの一部です:

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;

そして、これが私が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);
    });

  });

});

今、私はモーメントを使用しており、関数はモーメントを使用しているため、これらのテストに合格しましたが、少し不安定でテストの日付を固定時間に設定したいと思います。

それをどのように達成できるかについてのアイデアはありますか?

回答:


70

MockDateをjestテストで使用して、何をnew Date()返すかを変更できます。

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();

Datelikeの他の機能を使用していたので、うまくいきましたvalueOf()
Robin Zimmermann

143

momentjsはDate内部的に使用するため、Date.now関数を上書きして常に同じ瞬間を返すことができます。

Date.now = jest.fn(() => 1487076708000) //14.02.2017

または

Date.now = jest.fn(() => new Date(Date.UTC(2017, 1, 14)).valueOf())

34
返される実際の日付を設定する少しきれいな方法を次に示します。– 2017Date.now = jest.fn(() => new Date(Date.UTC(2017, 0, 1)).valueOf());
開発

4
または少しでもDate.now = jest.fn(() => +new Date('2017-01-01');
かわいい

3
または:Date.now = jest.fn(() => Date.parse('2017-02-14))
ジェレミーイートン

93

jest.spyOnはロック時間に対して機能します。

let dateNowSpy;

beforeAll(() => {
    // Lock Time
    dateNowSpy = jest.spyOn(Date, 'now').mockImplementation(() => 1487076708000);
});

afterAll(() => {
    // Unlock Time
    dateNowSpy.mockRestore();
});

3
素晴らしいソリューション。依存関係がなく、リセット可能にしておくと、単一のテストに簡単に適用できます。
Caleb Miller

14
dateNowSpy変数は必要ありません。jestjs.io/docs/en/mock-function-api.html#mockfnmockrestoreにmockReset()よると、これは冗長です。でafterAll、あなたは簡単に行うことができますDate.now.mockRestore()
ジミー

これはすばらしいので、追加のライブラリは必要ありません。しかし、これは、静的なDateメソッド(それほど多くはありません)を使用している場合にのみ機能します
hellatan

1
@Jimmy Date.now.mockRestore();プロパティ 'mockRestore'がタイプ '()=> number'に存在しないというエラーを
返す

3
@Marcoそれはjest.spyOn(Date、 "now")。mockRestore();である必要があります。
sab 2017

6

jest-date-mockは私が書いた完全なjavascriptモジュールであり、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
});

テストケースには3つのAPIのみを使用します。

  • advanceBy(ms):日付のタイムスタンプをmsずつ進めます。
  • advanceTo([timestamp]):日付をタイムスタンプにリセットします。デフォルトは0です。
  • clear():モックシステムをシャットダウンします。

あなたのケースは何ですか?
atool

5

新しいDateオブジェクトのメソッドをモックしたい場合は、次の操作を実行できます。

beforeEach(() => {
    jest.spyOn(Date.prototype, 'getDay').mockReturnValue(2);
    jest.spyOn(Date.prototype, 'toISOString').mockReturnValue('2000-01-01T00:00:00.000Z');
});

afterEach(() => {
    jest.restoreAll()
});

おかげで、これで私が抱えていた問題が修正されました。
グレイソンラングフォード

2

Date.now()一部のパッケージ(たとえばmoment.js)がnew Date()代わりに使用するため、モックのみに基づくすべての答えはどこでも機能しません。

この文脈では、に基づく答えMockDateは私が唯一正しいと思います。外部パッケージを使用したくない場合は、次のように直接記述できます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;

2

私はいくつかの代替アプローチを提供したいと思います。

スタブが必要な場合format()(ロケールとタイムゾーンに依存する可能性があります!)

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

スタブする必要があるだけの場合moment()

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

isDateToday上記の関数のテストに関して、私は最も簡単な方法はまったくモックmomentしないことだと思います


2
最初の例では、次のようになりますTypeError: moment.mockReturnValue is not a function
mkelley33

2
であるjest.mock("moment")あなたのimport文と同じレベルで?そうでなければ、あなたはそれが実際に動作しているのを見ることを歓迎しますこのプロジェクト
デヴィッド・

1

これは、私のDate.now()メソッドをモックして、テストの年を2010年に設定する方法です

jest
  .spyOn(global.Date, 'now')
  .mockImplementationOnce(() => new Date(`2010`).valueOf());

1

以下は、さまざまなユースケースに対応したいくつかの読みやすい方法です。他のコードで誤って上書きされる可能性がある、元のオブジェクトへの参照を保存するよりも、スパイを使用することを好みます。

1回限りのモック

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

いくつかのテスト

let dateSpy;

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

afterAll(() => {
  dateSpy.mockRestore();
});

0

手動モックを使用したいので、すべてのテストで使用できます。

// <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

0

目的は、テストのためにコンポーネントのレンダリング中に使用される場所に固定日付でnew Date()をモックすることです。new Date()fnをモックすることだけが必要な場合は、ライブラリを使用するとオーバーヘッドになります。

アイデアは、グローバル日付を一時変数に格納し、グローバルdaeをモックし、使用後、一時データをグローバル日付に再度割り当てることです。

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();
    });
});



答えも説明してください。コードのみを置くことは良いアプローチではありません
Intsab Haider

1
提案をありがとう。コメントで更新されました。
Pranava S Balugari

0

ここでチャイムを鳴らしたいと思ったのは、もしあなたが Date特定のスイートのみでオブジェクト。

各スイートのセットアップおよびティアダウンメソッドを使用してモックできます。jestdocs

/**
 * 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;
});

お役に立てれば!


0

date-fakerを使用できます。現在の日付を相対的に変更できます。

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();

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.