模拟导入会带来两个别名

时间:2019-01-14 03:09:41

标签: typescript momentjs jestjs

我试图用Jest编写单元测试,以利用moment.js库模拟代码的几个部分。为了完整起见,这是一个Node + Express项目,用TypeScript编写,带有支持的moment.d.ts文件。

我要测试的导入和代码块:

import moment from 'moment';

const durationSinceLastEmail = moment.duration(moment(new Date())
                .diff(moment(user.passwordRecoveryTokenRequestDate)));

为导入的弯矩参考提供的类型信息列出了两个实际项目:

(alias) function moment(inp?: moment.MomentInput, format?: moment.MomentFormatSpecification, strict?: boolean): moment.Moment (+1 overload)
(alias) namespace moment
import moment

我的实现代码使用两种形式:moment.duration用于名称空间,moment(...params)用于函数。

我一般的Jest模拟策略不是很有效。例)

jest.mock('moment', () => {
    return jest.fn().mockImplementation( () => {
        return {
            duration: (...params) => mockDuration(...params)
        };
    });
});

通过以一种更有力的方式直接替换duration方法,我成功地模拟了duration函数。

const originalDuration: Function = moment.duration;

mockDuration.mockImplementation((...params) => {
    const original = originalDuration(...params);
    // Apply mocks on returning object here
    // or supply entirely new mock object
    return original;
});

moment.duration = mockDuration;

坦率地说,代码相当粗糙,但是它使我半途而废,因为这使我可以捕获对moment.duration(...params)的调用,但是我尝试模拟moment(...)调用的每种方法都具有要么不起作用,要么与上面的方法完全冲突(并且也不起作用)。

命名冲突似乎是我遇到问题的根源,所以我的问题是:

1)无论如何,我是否有必要将这些不同的引用分开,以便可以对其进行明确处理?

2)是否有一种有效的方法可以分别模拟它们,或者在单个模拟对象中同时为函数和名称空间提供模拟?

1 个答案:

答案 0 :(得分:1)

您可以为moment创建一个Manual Mock,并为其提供所需的任何实现。

在项目的根目录下__mocks__/moment.js旁边创建node_modules

const momentMock = jest.fn();  // create the moment mock
momentMock.mockImplementation(() => ({ diff: jest.fn() }));  // give it a mock implementation

momentMock.duration = jest.fn();  // add the moment.duration mock

export default momentMock;  // export the mock

在测试中调用jest.mock('moment');以使用模拟:

import moment from 'moment';

jest.mock('moment');  // use the mock

test('durationSinceLastEmail', () => {
  const user = {
    passwordRecoveryTokenRequestDate: new Date('2019-01-01')
  }
  const durationSinceLastEmail = moment.duration(moment(new Date())
    .diff(moment(user.passwordRecoveryTokenRequestDate)));

  expect(moment).toHaveBeenCalledTimes(2);  // SUCCESS
  expect(moment.duration).toHaveBeenCalledTimes(1);  // SUCCESS
});