尝试了jest.fn, mock,spyOn三种方式都替换不了模块中调用的函数,究竟是哪里搞错了?

baidan4855
  • 1
新手上路,请多包涵

查了两天资料、issue最后总结有如题的三种方式可以部分替换模块中的某个函数,但死活就是没有效果,不知道问题到底是出在哪里了?有人用jest做过这种替换吗,请教

demo链接

代码如下

// src/module.js

export const A = () => "function A";

export const B = () => A();
// test/mock.test.js

import * as Module from "../src/module";

it("jest.fn", () => {
  Module.A = jest.fn().mockReturnValue("mock function");
  const result = Module.toHaveBeenCalled();
  expect(Module.A).toHaveBeenCalled();
  expect(result).toEqual("mock function");
});

it("spyOn", () => {
  const spy = jest.spyOn(Module, "A").mockImplementation(() => "mock function");
  const result = Module.B();
  expect(spy).toHaveBeenCalled();
  expect(result).toEqual("mock function");
});

it("mock", () => {
  jest.mock("../src/module", () => {
    const original = jest.requireActual("../src/module");
    return {
      __esModule: true,
      ...original,
      A: jest.fn(() => "mock function")
    };
  });
  const module = require("../src/module");

  const result = module.B();
  expect(module.A).toHaveBeenCalled();
  expect(result).toEqual("mock function");
});
回复
阅读 2.2k
1 个回答

模拟不起作用,是因为闭包的作用,B 中调用的并不是被模拟后的 A,而是实际的 A.

如果需要实现这种情况的模拟
一种方法是,修改源码如下

// src/module.js
export const A = () => 'function A'

export const B = () => exports.A()

然后去模拟 A,这样当 B 去执行时就会找到 exports 下的 A.

import * as Module from '../src/module'

it('spyOn', () => {
  const spy = jest.spyOn(Module, 'A').mockReturnValue('mock function')
  const result = Module.B()
  expect(spy).toHaveBeenCalled()
  expect(result).toEqual('mock function')
})

第二种方法是,拆分 module.js,将 A 拆分到另外一个文件 a.js 文件,然后将 A 导入到 module.js 中使用,这样直接去模拟 a.js 也可以.

第三种方法是,使用 Babel 插件,在编译的时候,添加一些额外的代码,实际上就是把需要手动去修改源码的过程,交给插件去自动完成了.
这样的插件有:

这样可以在不用动源代码的情况下,完成需要的模拟功能.在我的CodeSandbox例子中,用的是babel-plugin-explicit-exports-references

CodeSandbox

插件是否兼容项目,需要实际去测试才行.

另外建议可以配置 Babel 为只在测试环境中去使用插件.


更多详情 How to mock specific module function?

你知道吗?

宣传栏