能否在不修改原模块的前提下用Jest模拟JavaScript独立函数?
解决方案
可以实现,关键是利用Jest的模块模拟能力结合ES模块的绑定特性,在保留subtract原始实现的同时替换内部的add引用。
具体实现
修改math.test.js如下:
// 模拟整个math模块,同时保留真实的subtract实现 jest.mock('./math.js', () => { // 加载模块的真实代码 const original = jest.requireActual('./math.js'); return { ...original, // 将add替换为可模拟的jest函数 add: jest.fn(), }; }); // 导入模拟后的模块 import * as math from './math.js'; import { subtract } from './math.js'; describe('math', () => { it('should return 1234', () => { // 设置add的模拟返回值 math.add.mockReturnValue(1234); expect(subtract(5, 3)).toBe(1234); // 验证add是否被正确调用 expect(math.add).toHaveBeenCalledWith(5, -3); }); });
原理说明
jest.requireActual的作用:加载模块的真实代码,避免完全模拟丢失subtract的原始逻辑。- ES模块的绑定特性:ES模块的导出是动态绑定的,当我们在模拟对象中重写
add的导出时,模块内部subtract对add的引用会自动指向这个模拟函数——这正是常规spyOn无法做到的,因为spyOn只能拦截通过导出对象的属性访问,而模块内的直接调用原本绕过了导出对象。
为什么常规方法失效?
- 直接覆写导入的
add:ES模块的导出是只读的,无法直接赋值修改。 spyOn(math, 'add'):spyOn是替换导出对象的属性,但subtract内部调用的是模块本地定义的add函数,并非通过math.add引用,因此无法被拦截。
内容的提问来源于stack exchange,提问作者sstchur
相关产品推荐
相关产品推荐

