You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jest中父模块解构导入子方法时Mock失败的原因及解决

问题原因

当你用const { add } = require('./FileB')解构导入时,本质是把FileB导出对象里的add函数直接复制了一份到FileA的本地变量中。后续addTen调用的是这个本地复制的函数,和FileB导出对象上的add属性已经没有关联了。

Jest的spyOn是通过修改FileB导出对象的add属性,把它替换成mock函数。但FileA里的add变量早已经绑定了原始函数的引用,所以调用addTen时根本不会触发mock的那个函数,自然断言会失败。

而改成整体导入const fileB = require('./FileB')后,addTen调用的是fileB.add——这是直接引用FileB导出对象的add属性。当spyOn修改这个属性为mock函数时,addTen的调用就会指向mock版本,测试自然能通过。

保留解构导入的Mock方案

有两种可行的方法:

方法一:提前mock整个FileB模块

不用spyOn,直接用jest.mock在测试文件开头mock整个FileB模块,这样FileA在导入时解构得到的就是mock函数:

// randomTest.js
// 提前mock FileB模块,导出一个mock的add函数
jest.mock('./FileB', () => ({
  add: jest.fn()
}));

const { addTen } = require('./FileA');
const { add } = require('./FileB');

test('addTen 会调用 add 函数', () => {
  addTen(10);
  // 断言mock的add函数被正确调用
  expect(add).toHaveBeenCalledWith(10, 10);
});

这种方法不需要修改业务代码,完全在测试层解决问题。

方法二:在业务代码中做一层包装

如果不想修改测试逻辑,可以在FileA里把解构的add改成一个包装函数,始终指向FileB导出对象的add属性:

// FileA.js
const fileB = require('./FileB');
// 用包装函数保留对fileB.add的引用
const add = (...args) => fileB.add(...args);

const addTen = num => {
  return add(10, num);
};

module.exports = { addTen };

这样即使你在代码里写的是add调用,实际还是指向fileB.add,spyOn修改后就能生效。

内容的提问来源于stack exchange,提问作者Tanner Summers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 03:16:14