Jest模拟函数实例无法在模块间传递的问题排查与解决
问题分析与解决
为什么模拟函数没被调用?
核心原因是模块内部的函数引用和导出的函数引用脱节:
当你的模块(比如app.js)加载时,内部定义的sendToEH函数会被赋值给module.exports.sendToEH,但模块里的sendDataHandler直接引用的是内部的sendToEH变量,而不是exports.sendToEH。
你在测试中修改app.sendToEH = jest.fn(),只是替换了exports对象上的属性,但sendDataHandler调用的还是模块初始化时就绑定的原sendToEH函数,和你模拟的函数不是同一个引用。
至于第二个测试能通过,完全是因为你直接调用了app.sendToEH('asdf'),这个调用被模拟函数捕获到了,但sendDataHandler本身根本没调用这个模拟函数,属于假通过。
解决方法
根据你的代码结构,有几种可行的解决方案:
方案1:调整模块内部的调用方式
修改app.js里的sendDataHandler,让它调用导出的sendToEH,而不是直接引用内部变量:
// app.js function sendToEH(data) { // 原实现逻辑 } exports.sendToEH = sendToEH; exports.sendDataHandler = async (req, res, next) => { // 改为调用exports上的sendToEH await exports.sendToEH(req.data); };
这样修改后,你原来的测试代码就能正常工作,因为sendDataHandler会使用你替换后的模拟函数。
方案2:用Jest的jest.mock模拟整个依赖模块
如果sendToEH是抽离到单独的工具模块(比如eh-service.js),可以直接用jest.mock来模拟整个模块:
假设原代码结构:
// eh-service.js module.exports = { sendToEH: (data) => { // 原实现 } }; // app.js const { sendToEH } = require('./eh-service'); exports.sendDataHandler = async (req, res, next) => { await sendToEH(req.data); };
测试代码:
// app.test.js const app = require('./app'); const ehService = require('./eh-service'); // 模拟eh-service模块的sendToEH函数 jest.mock('./eh-service', () => ({ sendToEH: jest.fn() })); test('sendDataHandler triggers sendToEH', async () => { const req = { data: 'test-data' }; const res = {}; const next = jest.fn(); await app.sendDataHandler(req, res, next); expect(ehService.sendToEH).toHaveBeenCalledWith('test-data'); });
方案3:用jest.spyOn模拟实例/导出方法
如果你的app是类实例,或者sendDataHandler是通过this调用sendToEH的类方法,可以用jest.spyOn来替换目标方法:
// app.test.js const app = require('./app'); test('sendDataHandler calls sendToEH', async () => { // 替换app上的sendToEH为模拟函数,并返回Promise const sendToEHSpy = jest.spyOn(app, 'sendToEH').mockResolvedValue(); const req = { data: 'test' }; const res = {}; const next = jest.fn(); await app.sendDataHandler(req, res, next); expect(sendToEHSpy).toHaveBeenCalledWith('test'); // 测试后恢复原方法 sendToEHSpy.mockRestore(); });
内容的提问来源于stack exchange,提问作者Daniel Gros
相关产品推荐
相关产品推荐

