Jest 测试时如何正确 Mock 被测函数内部调用的同模块函数
Jest 同模块内部异步函数Mock失效问题解决方案
失效根因
之前所有mock尝试都不生效,本质是引用关系没有打通:
originalFunc在utils.js内部调用anotherFunc时,直接读取的是模块顶层作用域的原始函数引用,和模块对外导出的对象没有绑定关系- 测试用例里自己声明的
const anotherFunc = jest.fn(),属于测试文件的作用域变量,和utils.js模块内部的同名函数是完全独立的两个引用 - 之前写的
jest.mock替换的是utils模块对外导出的anotherFunc属性,完全影响不到模块内部作用域的原始函数引用,自然无法拦截真实调用。
可落地实现方案
方案1:最小侵入调整源文件引用方式
不需要大改业务逻辑,只需要把同模块内的函数调用改成从模块导出对象上读取引用,Jest的mock就能正常生效:
// utils.js // 按原有逻辑定义并导出两个函数 export async function anotherFunc(arg1, arg2) { // 原有业务逻辑 } export async function originalFunc(params) { // 把原来的裸调 anotherFunc() 改成从模块引用上取函数 await module.exports.anotherFunc(params.arg1, params.arg2) }
配合Jest的mock能力,测试文件可以这么写:
// utils.test.js // 导入被mock处理后的模块 const util = require('../src/utils') // 保留模块所有真实实现,只针对目标函数做替换 jest.mock('../src/utils', () => ({ ...jest.requireActual('../src/utils'), })) test('should test originalFunc', async () => { const params = { arg1: 'data1', arg2: 'data2', } // 直接在导入的模块对象上替换anotherFunc为mock实现 const mockAnotherFunc = jest.spyOn(util, 'anotherFunc').mockResolvedValue(undefined) await util.originalFunc(params) // 正常断言mock的调用参数 expect(mockAnotherFunc).toHaveBeenCalledWith('data1', 'data2') })
如果你的源文件是纯ES Module语法,把module.exports的引用换成import * as utils from './utils.js'后,内部调用写await utils.anotherFunc()即可,不需要其他改动。
方案2:零侵入业务代码的插件方案
如果不想改动任何业务代码,可以搭配babel-plugin-rewire这类编译工具插件,直接把模块内部的函数引用暴露为可替换的属性,在测试中直接调用插件提供的API替换内部函数的实现,完全不需要修改业务源文件。
常见踩坑提醒
- 不要在测试用例作用域里手动声明和内部函数同名的mock变量,这类变量和源文件模块内的函数没有任何绑定关系,永远不会被内部调用读到
- 调用mock函数的断言时,一定要通过
jest.spyOn拿到替换后的mock实例做校验,不要直接引用自己声明的未关联的mock变量
内容的提问来源于stack exchange,提问作者navig8tr
相关产品推荐
相关产品推荐

