直接调用函数与通过exports对象调用的差异及Jest Mock问题解析
为什么Jest Mock函数b后,函数a调用b()不生效但调用exports.b()生效?
核心原因:模块内部函数引用 vs 导出对象的函数引用差异
1. 原始代码中a()直接调用b()的情况
你的Module.js里,function b()是定义在模块作用域内的局部函数,a()直接调用的是这个局部的b函数。当你用export {a, b}导出时,本质是把b函数的引用复制到了模块的导出对象上(ES模块里是创建导出绑定,行为类似)。
Jest的jest.spyOn(module, 'b')操作的是导出对象上的b引用,但模块内部a()调用的还是原来的局部b函数——两者是独立的引用,所以Mock不会影响a()的执行结果。
用代码逻辑拆解:
// Module.js 实际执行逻辑简化 const b = function() { return 'b'; }; const a = function() { return b(); }; // 这里的b是指向原始函数的局部变量 // 导出时,把b的引用复制到导出对象 module.exports.b = b; module.exports.a = a;
当你在测试中jest.spyOn(module, 'b').mockReturnValue('c'),只是把module.exports.b的引用换成了Mock函数,但a()内部还是调用原来的b,所以返回值还是'b'。
2. 修改为exports.b()后的情况
当你把a()内部的调用改成exports.b()(或ES模块里的import * as self from './Module.js'; self.b()),此时a()调用的是导出对象上的b函数引用。
这时候Jest的spyOn修改的正是这个导出对象上的b引用,所以当a()执行时,会直接调用Mock后的函数,返回预期的'c'。
逻辑拆解:
// 修改后的Module.js 逻辑简化 const b = function() { return 'b'; }; const a = function() { return module.exports.b(); }; // 直接引用导出对象上的b module.exports.b = b; module.exports.a = a;
测试中Mockmodule.exports.b后,a()调用的就是Mock函数,返回'c'。
两种调用方式的核心差异总结
- 直接调用
b():使用模块作用域内的原始函数引用,和导出对象上的引用相互独立,Mock导出的函数不会影响内部调用。 - 调用
exports.b():直接使用导出对象上的函数引用,Mock操作会直接覆盖这个引用,内部调用会同步使用Mock后的函数。
内容的提问来源于stack exchange,提问作者Dummy Jonson
相关产品推荐
相关产品推荐

