Jest同模块下jest.mock()部分mock单函数失效原因及解决
问题根本原因
这个现象的核心是JS模块的作用域引用规则,和你推测的「两个函数同属一个模块」方向一致,具体逻辑如下:
main.ts里定义的add、calculate首先是模块作用域下的本地函数,calculate内部调用add时,直接使用的是当前闭包内持有的原始add函数引用,完全不会经过模块的导出对象。- 你用
jest.mock配合jest.requireActual的写法,本质是先拿到原模块的完整导出对象,再把导出对象上的add属性替换为自定义的mock函数。但这个修改只作用于导出对象,根本碰不到calculate闭包里存的那份原始add引用,所以执行calculate时,调用的还是未被mock的原始add,自然返回计算结果5,而不是你配置的17。
补充说明:你提到的jest.spyOn方案如果能生效,本质是你的TS/JS编译配置把模块内的函数调用编译成了exports.add()形式(即调用导出对象上的属性),这种场景下spyOn修改导出对象属性才能命中调用,否则spyOn同样会失效。
对应修复方案(坚持使用
jest.mock方式的前提下) 核心思路是调整模块内的函数调用逻辑,让同模块内的互调走模块导出对象,而不是直接调用本地闭包内的函数,这样jest对导出对象的修改才能生效。
你可以修改main.ts的写法,参考如下实现:
// main.ts export const add = (x: number, y: number): number => { return x + y; } export const minus = (x: number, y: number): number => { return x - y; } // 引入当前模块的导出对象 import * as self from './main'; export const calculate = (x: number, y: number, operator: string) => { let result = -1; switch (operator) { case '+': // 调用导出对象上的add,而非本地闭包持有的原始add result = self.add(x, y); break; case '-': result = self.minus(x, y); break; } return result; }
修改完成后,你原来的测试代码不需要调整,运行时calculate就会调用到你mock的返回17的add函数。
如果不想修改业务源码,就只能用jest.spyOn配合对应的编译配置,或者拆分模块把add、minus抽到单独的文件里——这种场景下calculate调用add时走的是跨模块导入引用,jest.mock就可以正常拦截。
内容的提问来源于stack exchange,提问作者omer727
相关产品推荐
相关产品推荐

