如何使用Jest监视默认的导出功能?

thi*_*ign 12 unit-testing mocking spy reactjs jestjs

假设我有一个导出默认功能的简单文件:

// UniqueIdGenerator.js
const uniqueIdGenerator = () => Math.random().toString(36).substring(2, 8);

export default uniqueIdGenerator;
Run Code Online (Sandbox Code Playgroud)

我会这样使用:

import uniqueIdGenerator from './UniqueIdGenerator';
// ...
uniqueIdGenerator();
Run Code Online (Sandbox Code Playgroud)

我想在测试中断言在保持原始功能的同时调用了此方法。jest.spyOn但是,我需要一个对象以及一个函数名称作为参数。您如何以一种干净的方式做到这一点?还有一个类似的GitHub的问题jasmine兴趣的人。

Hub*_*ris 36

这是一种默认导出的方法,无需修改导入(甚至在测试中根本不需要导入):

const actual = jest.requireActual("./UniqueIdGenerator");
const spy = jest.spyOn(actual, "default");
Run Code Online (Sandbox Code Playgroud)

  • 对我来说最干净的答案 (2认同)

小智 29

还可以模拟导入并将原始实现作为模拟实现传递,例如:

import uniqueIdGenerator from './UniqueIdGenerator'; // this import is a mock already

jest.mock('./UniqueIdGenerator.js', () => {
  const original = jest. requireActual('./UniqueIdGenerator')
  return {
     __esModule: true,
     default: jest.fn(original.default)
  }
})

test(() => {
  expect(uniqueIdGenerator).toHaveBeenCalled()
})
Run Code Online (Sandbox Code Playgroud)


thi*_*ign 15

我最终放弃了默认导出:

// UniqueIdGenerator.js
export const uniqueIdGenerator = () => Math.random().toString(36).substring(2, 8);
Run Code Online (Sandbox Code Playgroud)

然后我可以像这样使用和监视它:

import * as UniqueIdGenerator from './UniqueIdGenerator';
// ...
const spy = jest.spyOn(UniqueIdGenerator, 'uniqueIdGenerator');
Run Code Online (Sandbox Code Playgroud)

一些建议将它们包装在const对象中,然后导出。我想您也可以使用包装类。

但是,如果您不能修改该类,那么仍然有一个(不太好的)解决方案:

import * as UniqueIdGenerator from './UniqueIdGenerator';
// ...
const spy = jest.spyOn(UniqueIdGenerator, 'default');
Run Code Online (Sandbox Code Playgroud)

  • 最后的代码片段实际上是解决方案! (6认同)
  • 我喜欢 `const sp = jest.spyOn(UniqueIdGenerator, 'default');` 因为你不需要重构任何代码并冒发生某些事情的风险...... (4认同)
  • 但这仅在 babel 通过 es6 模块转译时才有效。不适用于 CommonJS (3认同)
  • 对于安装在 node_modules 中的模块,这对我不起作用。我尝试了第二个解决方案,如下所示:`import * as fetchModule from 'cross-fetch'`。然后使用“jest.spyOn(fetchmodule, 'default')”进行监视。我收到错误消息:“无法分配给对象“[object Object]”的只读属性“default”” (3认同)
  • 谢谢。Typescript/Nodejs/Jest 简直是一种痛苦。 (2认同)

小智 12

使用“default”作为sp​​yOn 函数中的第二个参数。

import * as MyHelperMethod from '../myHelperMethod';

jest.spyOn(MyHelperMethod, 'default');
Run Code Online (Sandbox Code Playgroud)


dec*_*ele 10

仅模拟默认导出或任何其他导出,但将模块中的其余导出保留为原始导出:

import myDefault, { myFunc, notMocked } from "./myModule";

jest.mock("./myModule", () => {
  const original = jest.requireActual("./myModule");
  return {
    __esModule: true,
    ...original,
    default: jest.fn(),
    myFunc: jest.fn()
  }
});

describe('my description', () => {
  it('my test', () => {
    myFunc();
    myDefault();
    expect(myFunct).toHaveBeenCalled();
    expect(myDefault).toHaveBeenCalled();
    
    myDefault.mockImplementation(() => 5);
    expect(myDefault()).toBe(5);
    expect(notMocked()).toBe("i'm not mocked!");
  })
});
Run Code Online (Sandbox Code Playgroud)

  • 很不错!就我个人而言,我仍然需要让默认方法返回其原始值,因此我在 `jest.fn()` 中调用它,如下所示: `default: jest.fn((...args) => original(...args) )`。 (2认同)

Jan*_*ala 6

在某些情况下,您必须模拟导入才能监视默认导出:

import * as fetch from 'node-fetch'

jest.mock('node-fetch', () => ({
  default: jest.fn(),
}))

jest.spyOn(fetch, 'default')
Run Code Online (Sandbox Code Playgroud)

  • @kylejw2,当我这样做时,我收到“无法分配给对象“[object Object]”的只读属性“默认””。 (4认同)
  • 在这个用例中,“jest.mock”不是必需的。你也可以写 `jest.spyOn(fetch, 'default').mockImplementation(jest.fn())` (3认同)