如何在Jest中设置模拟日期?

ale*_*gel 86 momentjs jestjs

我正在使用moment.js在我的React组件的辅助文件中执行大部分日期逻辑,但是我还没弄清楚如何在Jest a sinon.useFakeTimers()中模拟日期.

Jest文档只讨论定时器函数,如setTimeout,setInveral等,但没有帮助设置日期,然后检查我的日期函数是否执行他们想要做的事情.

这是我的一些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;
Run Code Online (Sandbox Code Playgroud)

这是我用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);
    });

  });

});
Run Code Online (Sandbox Code Playgroud)

现在这些测试通过,因为我正在使用时刻,我的函数使用时刻,但似乎有点不稳定,我想将日期设置为测试的固定时间.

有关如何实现这一点的任何想法?

Sim*_*enB 120

从 Jest 26 开始,这可以使用“现代”假定时器来实现,而无需安装任何 3rd 方模块:https : //jestjs.io/blog/2020/05/05/jest-26#new-fake-timers

jest
  .useFakeTimers('modern')
  .setSystemTime(new Date('2020-01-01').getTime());
Run Code Online (Sandbox Code Playgroud)

如果您希望所有测试的假计时器都处于活动状态,您可以timers: 'modern'在您的配置中进行设置:https : //jestjs.io/docs/en/configuration#timers-string

  • 完成假计时器后,您可能需要 [`jest.useRealTimers()`](https://jestjs.io/docs/en/jest-object#jestuserealtimers)。 (8认同)
  • 另外值得注意的是,`.useFakeTimers('modern')` 位可以从全局配置文件(如 `setupTests.js`)调用。因此,一旦成为默认选项,就可以轻松删除它。来自同一个链接:“在 Jest 27 中,我们将把默认值替换为新的“现代””。 (5认同)
  • 仅当我在测试设置中调用“jest.setSystemTime()”时,此解决方案才适用于我;如果我在测试套件的“beforeAll”中调用它,它将被忽略。检查我为测试此创建的存储库 https://github.com/dariospadoni/jestFakeTimersMock/blob/main/src/setupTests.js (5认同)
  • 谢谢,我认为这应该是这个问题的解决方案。 (3认同)
  • 这绝对是解决问题的最简单的方法 (2认同)

ste*_*nis 114

由于momentjs在Date内部使用,您只需覆盖该Date.now函数即可始终返回相同的时刻.

Date.now = jest.fn(() => 1487076708000) //14.02.2017
Run Code Online (Sandbox Code Playgroud)

  • 设置将返回的实际日期有点漂亮:`Date.now = jest.fn(()=> new Date(Date.UTC(2017,0,1)).valueOf());` (31认同)
  • 这会正确模拟“日期”的所有使用吗?像“new Date()”一样? (5认同)
  • 恢复它怎么办?所以其他测试可以使用`Date.now()` (5认同)
  • 或者:`Date.now = jest.fn(() => Date.parse('2017-02-14))` (4认同)
  • 甚至更漂亮:`Date.now = jest.fn(()=> + new Date('2017-01-01');`) (2认同)

Tim*_*ord 73

jest.spyOn适用于锁定时间:

let dateNowSpy;

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

afterAll(() => {
    // Unlock Time
    dateNowSpy.mockRestore();
});
Run Code Online (Sandbox Code Playgroud)

  • 根据https://jestjs.io/docs/en/mock-function-api.html#mockfnmockrestore,不需要`dateNowSpy`变量,而`mockReset()`是多余的.在`afterAll`中,你可以简单地做`Date.now.mockRestore()` (8认同)
  • 好的解决方案 没有依赖关系并保持可重置性使其易于应用于单个测试. (2认同)
  • @Jimmy `Date.now.mockRestore();` 给出 _Property 'mockRestore' does not exit on type '() => number'_ 错误 (2认同)
  • @Marco应该是jest.spyOn(Date,“ now”)。mockRestore(); (2认同)

小智 52

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();
Run Code Online (Sandbox Code Playgroud)

  • 效果很好,因为我使用了其他的 `Date` 函数,比如 `valueOf()`。 (2认同)

Rob*_*yes 24

对于那些想要在new Date对象上模拟方法的人,您可以执行以下操作:

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

afterEach(() => {
    jest.restoreAll()
});
Run Code Online (Sandbox Code Playgroud)


Duc*_*Mai 10

这对我有用:

const mockDate = new Date('14 Oct 1995')
global.Date = jest.fn().mockImplementation(() => mockDate) // mock Date "new" constructor
global.Date.now = jest.fn().mockReturnValue(mockDate.valueOf()) // mock Date.now
Run Code Online (Sandbox Code Playgroud)


Cle*_*ter 8

所有仅基于模拟的答案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;
Run Code Online (Sandbox Code Playgroud)


Rob*_*per 6

Jest版本开始29,您还可以执行以下操作:

jest.useFakeTimers({
  now: 1673445238335,
});
Run Code Online (Sandbox Code Playgroud)

允许使用以下选项:

type FakeTimersConfig = {
  /**
   * If set to `true` all timers will be advanced automatically by 20 milliseconds
   * every 20 milliseconds. A custom time delta may be provided by passing a number.
   * The default is `false`.
   */
  advanceTimers?: boolean | number;
  /**
   * List of names of APIs that should not be faked. The default is `[]`, meaning
   * all APIs are faked.
   */
  doNotFake?: Array<FakeableAPI>;
  /**
   * Use the old fake timers implementation instead of one backed by `@sinonjs/fake-timers`.
   * The default is `false`.
   */
  legacyFakeTimers?: boolean;
  /** Sets current system time to be used by fake timers. The default is `Date.now()`. */
  now?: number | Date;
  /**
   * The maximum number of recursive timers that will be run when calling `jest.runAllTimers()`.
   * The default is `100_000` timers.
   */
  timerLimit?: number;
};

Run Code Online (Sandbox Code Playgroud)

您可以在文档中阅读更多内容。


ato*_*ool 5

jest-date-mock是我编写的完整的javascript模块,用于测试jest上的Date。

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
});
Run Code Online (Sandbox Code Playgroud)

仅将3个API用于测试用例。

  • advanceBy(ms):提前日期时间戳,单位为ms。
  • advanceTo([timestamp]):将日期重置为时间戳,默认为0。
  • clear():关闭模拟系统。


小智 5

这就是我如何模拟Date.now()将测试年份设置为 2010 年的方法

jest
  .spyOn(global.Date, 'now')
  .mockImplementationOnce(() => new Date(`2010`).valueOf());
Run Code Online (Sandbox Code Playgroud)


Yan*_*Tay 5

以下是针对不同用例的几种可读方式。我更喜欢使用间谍而不是保存对原始对象的引用,原始对象可能会被其他一些代码意外覆盖。

一次性嘲讽

jest
  .spyOn(global.Date, 'now')
  .mockImplementationOnce(() => Date.parse('2020-02-14'));
Run Code Online (Sandbox Code Playgroud)

一些测试

let dateSpy;

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

afterAll(() => {
  dateSpy.mockRestore();
});
Run Code Online (Sandbox Code Playgroud)