我使用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,但它似乎有点不稳定,我想将日期设置为测试的固定时间。

你知道该怎么做吗?


当前回答

在我的情况下,我必须在测试前模拟整个Date和'now'函数:

const mockedData = new Date('2020-11-26T00:00:00.000Z');

笑话。spyOn(global, 'Date').mockImplementation(() => mockedData);

Date.now = () => 1606348800;

describe(测试)=> {.h.}

其他回答

仅基于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;

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():关闭模拟系统。

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

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

公认的答案很好

Date.now = jest.fn().mockReturnValue(new Date('2021-08-29T18:16:19+00:00'));

但是如果我们想在管道中运行单元测试,我们必须确保我们使用相同的时区。要做到这一点,我们必须模拟时区以及

jest.config.js

process.env.TZ = 'GMT';

module.exports = {
 ...
};

请参见:时区的完整列表(列TZ数据库名)

下面的测试存根Date在测试生命周期中返回一个常量。

如果你在你的项目中使用了new Date(),那么你可以在你的测试文件中模拟它,如下所示:

  beforeEach(async () => {
    let time_now = Date.now();
    const _GLOBAL: any = global;
    _GLOBAL.Date = class {
      public static now() {
        return time_now;
      }
    };
}

现在,无论您在测试文件中使用new Date(),它都会产生相同的时间戳。

注意:你可以用beforeAll替换beforeEach。而_GLOBAL只是一个满足typescript的代理变量。

完整的代码我尝试:

let time_now;
const realDate = Date;

describe("Stubbed Date", () => {
  beforeAll(() => {
    timeNow = Date.now();
    const _GLOBAL: any = global;
    _GLOBAL.Date = class {
      public static now() {
        return time_now;
      }

      constructor() {
        return time_now;
      }

      public valueOf() {
        return time_now;
      }
    };
  });

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

  it("should give same timestamp", () => {
    const date1 = Date.now();
    const date2 = new Date();
    expect(date1).toEqual(date2);
    expect(date2).toEqual(time_now);
  });
});

这对我很管用。