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
相关产品推荐
相关产品推荐

