我使用moment.js在我的React组件的帮助文件中做大部分的日期逻辑,但我还没有弄清楚如何在Jest a la sinon.useFakeTimers()中模拟日期。

Jest文档只谈到了计时器函数,如setTimeout, setInterval等,但没有帮助设置日期,然后检查我的日期函数是否做了他们应该做的事情。

这是我的一些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);
    });
    
  });

});

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

你知道该怎么做吗?


当前回答

为了快速和肮脏的解决方法使用笑话。锁定时间:

let dateNowSpy;

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

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

更新:

要获得更健壮的解决方案,请查看timekeeper:

import timekeeper from 'timekeeper';

beforeAll(() => {
    // Lock Time
    timekeeper.freeze(new Date('2014-01-01'));
});

afterAll(() => {
    // Unlock Time
    timekeeper.reset();
});

其他回答

截至Jest 26,这可以使用“现代”假计时器来实现,而不需要安装任何第三方模块:https://jestjs.io/blog/2020/05/05/jest-26#new-fake-timers

jest
  .useFakeTimers()
  .setSystemTime(new Date('2020-01-01'));

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

编辑:截至Jest 27,现代假计时器是默认的,所以你可以删除参数useFakeTimers。

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

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

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

joke - Date -mock是我自己编写的一个完整的javascript模块,用来测试Date在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():关闭模拟系统。

@pranava-s-balugari的回复有所改善

它不影响新日期(某事) 模拟日期可以更改。 它将工作日期。现在太

const DateOriginal = global.Date;

global.Date = class extends DateOriginal {
    constructor(params) {
        if (params) {
          super(params)
        } else if (global.Date.NOW === undefined) {
          super()
        } else {
          super(global.Date.NOW)
        }
    }
    static now () {
      return new Date().getTime();
    }
}

afterEach(() => {
  global.Date.NOW = undefined;
})

afterAll(() => {
  global.Date = DateOriginal;
});

describe('some test', () => {
  afterEach(() => NOW = undefined);

  it('some test', () => {
     Date.NOW = '1999-12-31T23:59:59' // or whatever parameter you could pass to new Date([param]) to get the date you want


     expect(new Date()).toEqual(new Date('1999-12-31T23:59:59'));
     expect(new Date('2000-01-01')).toEqual(new Date('2000-01-01'));
     expect(Date.now()).toBe(946681199000)

     Date.NOW = '2020-01-01'

     expect(new Date()).toEqual(new Date('2020-01-01'));
  })
})

为了快速和肮脏的解决方法使用笑话。锁定时间:

let dateNowSpy;

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

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

更新:

要获得更健壮的解决方案,请查看timekeeper:

import timekeeper from 'timekeeper';

beforeAll(() => {
    // Lock Time
    timekeeper.freeze(new Date('2014-01-01'));
});

afterAll(() => {
    // Unlock Time
    timekeeper.reset();
});