如何模拟像New Date()这样的构造函数

Set*_*amp 18 javascript jestjs

我有一个方法,它依赖于new Date创建一个日期对象,然后操纵它.我正在测试操作是否按预期工作,因此我需要将返回的日期与预期日期进行比较.为了做到这一点,我需要确保new Date在测试和被测试的方法中返回相同的值.我怎样才能做到这一点?

有没有办法实际模拟构造函数的返回值?

我可以使用提供日期对象的函数创建一个模块,并且可以进行模拟.但这似乎是我代码中不必要的抽象.

一个要测试的示例函数......

module.exports = {
  sameTimeTomorrow: function(){
    var dt = new Date();
        dt.setDate(dt + 1);
    return dt;
  }
};
Run Code Online (Sandbox Code Playgroud)

我该如何模拟返回值new Date()

Yuc*_*uci 78

更新:此答案是jest < version 26查看最近玩笑版本的此答案的方法。


您可以使用jest.spyOn如下方法模拟像 new Date() 这样的构造函数:

test('mocks a constructor like new Date()', () => {
  console.log('Normal:   ', new Date().getTime())

  const mockDate = new Date(1466424490000)
  const spy = jest
    .spyOn(global, 'Date')
    .mockImplementation(() => mockDate)

  console.log('Mocked:   ', new Date().getTime())
  spy.mockRestore()

  console.log('Restored: ', new Date().getTime())
})
Run Code Online (Sandbox Code Playgroud)

输出如下所示:

Normal:    1566424897579
Mocked:    1466424490000
Restored:  1566424897608
Run Code Online (Sandbox Code Playgroud)

请参阅GitHub 上的参考项目

注意:如果您使用的是 TypeScript 并且会遇到编译错误,Argument of type '() => Date' is not assignable to parameter of type '() => string'. Type 'Date' is not assignable to type 'string'. 在这种情况下,解决方法是使用mockdate库,该库可用于更改“现在”的时间。有关更多详细信息,请参阅此问题

  • 我得到“'() =&gt; Date'类型的参数不能分配给'() =&gt; string'类型的参数”。类型“日期”不可分配给类型“字符串”。` (12认同)
  • 对于 TS,你可以欺骗 Jest 和 TS:.`mockImplementation(() =&gt; mockDate asknown as string);`。像“expect(new Date()).toBe(mockDate);”这样的测试有效...... (10认同)

Thd*_*hdK 47

从 jest 26 开始,您可以使用支持该方法的“现代”fakeTimers 实现(请参阅此处的文章jest.setSystemTime

beforeAll(() => {
    jest.useFakeTimers('modern');
    jest.setSystemTime(new Date(2020, 3, 1));
});

afterAll(() => {
    jest.useRealTimers();
});
Run Code Online (Sandbox Code Playgroud)

请注意,这'modern'将是 jest 版本 27 的默认实现。

请参阅setSystemTime 此处的文档。

  • 这会导致我的测试出现超时错误 (11认同)
  • 请注意,使用假定时器会影响 `nock` 并导致它错过 HTTP 匹配。我浪费了很多时间,直到我发现假时间是导致我的规格失败的原因。 (6认同)

小智 10

尽管其他答案解决了问题,但我发现它更自然,并且通常适用于仅模拟 Date 的“无参数构造函数”行为,同时保持 Date 的其他功能完好无损。例如,当 ISO 日期字符串传递给构造函数时,期望返回此特定日期而不是模拟日期可能是合理的。

test('spies new Date(...params) constructor returning a mock when no args are passed but delegating to real constructor otherwise', () => {
    const DateReal = global.Date;
    const mockDate = new Date("2020-11-01T00:00:00.000Z");

    const spy = jest
        .spyOn(global, 'Date')
        .mockImplementation((...args) => {
            if (args.length) {
                return new DateReal(...args);
            }
            return mockDate;
        })
        
    const dateNow = new Date();

    //no parameter => mocked current Date returned
    console.log(dateNow.toISOString()); //outputs: "2020-11-01T00:00:00.000Z"

    //explicit parameters passed => delegated to the real constructor
    console.log(new Date("2020-11-30").toISOString()); //outputs: "2020-11-30T00:00:00.000Z"
    
    //(the mocked) current Date + 1 month => delegated to the real constructor
    let dateOneMonthFromNow = new Date(dateNow);
    dateOneMonthFromNow.setMonth(dateNow.getMonth() + 1);
    console.log(dateOneMonthFromNow.toISOString()); //outputs: "2020-12-01T00:00:00.000Z"

    spy.mockRestore();
}); 
Run Code Online (Sandbox Code Playgroud)


lin*_*dan 9

您可以使用茉莉花的spyOn(jest是在茉莉花上构建的)来模拟Date的getDate原型,如下所示:

spyOn(Date.prototype, 'setDate').and.returnValue(DATE_TO_TEST_WITH);
Run Code Online (Sandbox Code Playgroud)

SpyOn自身完成后也将清除,并且仅在测试范围内持续。


小智 7

您可以使用模拟函数覆盖Date构造函数,该函数返回具有指定日期值的构造好的Date对象:

var yourModule = require('./yourModule')

test('Mock Date', () => {
  const mockedDate = new Date(2017, 11, 10)
  const originalDate = Date

  global.Date = jest.fn(() => mockedDate)
  global.Date.setDate = originalDate.setDate

  expect(yourModule.sameTimeTomorrow().getDate()).toEqual(11)
})
Run Code Online (Sandbox Code Playgroud)

您可以在此处测试示例:https : //repl.it/@miluoshi5/jest-mock-date