【问题标题】:Mocking import which brings in two aliases引入两个别名的模拟导入
【发布时间】:2019-01-14 03:09:40
【问题描述】:

我正在尝试在 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)
        };
    });
});

我已经成功地模拟了持续时间函数,方法是直接替换持续时间方法,以更强有力的方式。

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) 我有没有一种有效的方法来分别模拟它们,或者在单个模拟对象中同时为函数和命名空间提供模拟?

【问题讨论】:

  • 听起来您最感兴趣的是监视对 moment()moment.duration() 的调用,而对替换它们的实现不太感兴趣,对吗?
  • 监视这些很好-但是,我想模拟diff()函数的实现(在从moment()返回的对象上提供)-我一直在尝试模拟moment()为了访问这个方法。

标签: typescript momentjs jestjs


【解决方案1】:

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

在您项目的根目录下创建__mocks__/moment.jsnode_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
});

【讨论】:

  • 这非常有效。我必须做的唯一更改是将导出转换为 CommonJS 语法,因为它是 JS 文件而不是 TS 文件。 module.exports = momentMock; // export the mock
猜你喜欢
  • 1970-01-01
  • 2015-12-14
  • 1970-01-01
  • 2018-01-23
  • 2022-08-17
  • 1970-01-01
  • 1970-01-01
  • 2018-03-27
  • 2012-08-04
相关资源
最近更新 更多