同一文件内函数Mock失效:测试中Mock可见但组件仍调用原函数
同一文件内组件调用的方法Mock失效问题
问题场景
尝试Mock被测组件调用的同文件方法,但Mock不生效:组件依然调用原函数,断言失败,原函数的日志也会输出。
被测文件代码
export function trackErrors() { console.log("This SHOULD NOT BE SEEN ON tests"); // 目前运行测试时这条日志会被打印 } // 待测试组件 export const form = () => { return <FormComponent callback={trackErrors} /> }
测试代码(Mock方案)
jest.mock('./testedFile', () => { const actualModule = jest.requireActual('./testedFile') return new Proxy(actualModule, { get: (target, property) => { switch (property) { case 'trackErrors': { return jest.fn() } default: { return target[property] } } }, }) }) import * as SModule from "./testedFile"; it.only("Calls trackErrors", async () => { const { getByTestId } = render(<SModule.form />); await typeEmail(getByTestId, "abcd"); await waitFor(() => { expect(SModule.trackErrors).toHaveBeenCalledTimes(1); }); });
问题现象
- 打印
SModule.trackErrors能看到是Mock函数,但断言调用次数为0失败 - 原函数的
console.log输出可见,说明组件实际调用的是未Mock的原函数 - 尝试
spyOn方案,结果完全相同
原因分析
核心问题是模块内直接引用:组件form定义时,直接通过本地标识符trackErrors绑定了原函数,而非通过模块导出的引用。Jest的Mock仅能替换模块对外导出的内容,无法修改模块内部已经建立的直接引用关系。
解决方案
方案1:通过模块自身引用导出的方法
修改被测文件,让组件通过模块导出对象引用trackErrors,这样Mock替换导出后,组件会自动使用Mock版本:
// ES模块写法:导入自身模块 import * as self from './testedFile'; export function trackErrors() { console.log("This SHOULD NOT BE SEEN ON tests"); } export const form = () => { return <FormComponent callback={self.trackErrors} /> }
如果是CommonJS环境,替换为module.exports.trackErrors即可。
方案2:将方法抽离到独立模块
把trackErrors移到单独模块,被测文件和测试都导入该模块,Mock这个独立模块即可生效:
// utils/trackErrors.js export function trackErrors() { console.log("This SHOULD NOT BE SEEN ON tests"); } // testedFile.js import { trackErrors } from './utils/trackErrors'; export const form = () => { return <FormComponent callback={trackErrors} /> }
测试代码修改为:
jest.mock('./utils/trackErrors', () => ({ trackErrors: jest.fn() })); import * as TrackUtils from './utils/trackErrors'; import { form } from './testedFile'; it.only("Calls trackErrors", async () => { const { getByTestId } = render(<form />); await typeEmail(getByTestId, "abcd"); await waitFor(() => { expect(TrackUtils.trackErrors).toHaveBeenCalledTimes(1); }); });
方案3:使用Jest rewire(仅CommonJS)
如果项目基于CommonJS,可以用jest-rewire修改模块内部变量,但ES模块不支持这种方式,仅作为临时解决方案,不推荐长期使用。
内容的提问来源于stack exchange,提问作者Chayemor
相关产品推荐
相关产品推荐

