You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何SpyOn其他文件本地函数及解决内部调用导出函数SpyOn失效问题

单元测试中SpyOn函数的常见问题解决

嘿,我来帮你搞定这两个单元测试里的spy问题,咱们先从你给出的具体场景入手,再延伸到另一个问题。

问题2:SpyOn同文件中被内部调用的导出函数

你当前遇到的情况是:myFunction里直接调用了同文件的myOtherFunction,但spy检测不到这个调用。问题根源在于:myFunction内部调用的是本地定义的myOtherFunction引用,而你spyOn(helpers, 'myOtherFunction')替换的是模块导出对象上的函数引用——这俩不是同一个引用,所以spy抓不到内部调用。

解决方案分两种:

方案1:修改业务代码,让函数通过模块对象调用

把myFunction里的调用改成引用模块导出的版本,这样spy替换导出对象上的函数后,内部调用就会用被spy的版本:

// File 1
export function myFunction(){
  // 用exports或者导入自身模块来调用
  return exports.myOtherFunction();
  // 或者:
  // import * as self from './helpers';
  // return self.myOtherFunction();
}
export function myOtherFunction(){
  // do stuff
}

这样你的测试代码不需要修改,就能正常检测到调用了。

方案2:不修改业务代码,用工具替换模块内部的本地函数

如果不想动业务代码,可以用rewire(支持Mocha/Jest等)或者Jest的内置能力来直接修改模块内部的本地函数:

  • 用rewire的示例:
// 测试文件
const helpers = rewire('./helpers');

beforeEach(() => {
  // 替换模块内部的myOtherFunction为spy
  const myOtherFunctionSpy = jest.fn();
  helpers.__set__('myOtherFunction', myOtherFunctionSpy);
});

it('should call myOtherFunction', () => {
  helpers.myFunction();
  // 获取被替换的spy并断言
  const myOtherFunctionSpy = helpers.__get__('myOtherFunction');
  expect(myOtherFunctionSpy).toHaveBeenCalled();
});
  • 用Jest的jest.mock结合requireActual的示例:
// 测试文件
import * as helpers from './helpers';

// 重写模块,保留原始导出,只替换myOtherFunction为spy
jest.mock('./helpers', () => {
  const originalModule = jest.requireActual('./helpers');
  return {
    ...originalModule,
    myOtherFunction: jest.fn(),
    // 关键:让myFunction调用的是这个被spy的版本
    myFunction: () => originalModule.myFunction.call(helpers)
  };
});

it('should call myOtherFunction', () => {
  helpers.myFunction();
  expect(helpers.myOtherFunction).toHaveBeenCalled();
});

问题1:SpyOn其他文件中的本地(未导出)函数

如果要spy的是其他文件里没导出的本地函数(比如File1里有个function localFunc() {}没导出,被导出函数调用),核心思路是访问并替换模块内部的私有变量,常用的方法:

  • 用rewire工具:和上面方案2类似,通过__set__方法直接替换模块内部的未导出函数,测试逻辑和之前一致。
  • 用Babel插件:比如babel-plugin-rewire,在编译时给模块添加访问私有变量的方法,用法和rewire类似。
  • Jest环境下,可以用jest.mock完全重写模块,把未导出函数的逻辑替换成spy,同时保留其他导出函数的逻辑。

举个rewire的示例(假设File1有未导出的localFunc被myFunction调用):

// File1
function localFunc() {
  // do stuff
}
export function myFunction() {
  return localFunc();
}

// 测试文件
const helpers = rewire('./helpers');

beforeEach(() => {
  const localFuncSpy = jest.fn();
  helpers.__set__('localFunc', localFuncSpy);
});

it('should call localFunc', () => {
  helpers.myFunction();
  const localFuncSpy = helpers.__get__('localFunc');
  expect(localFuncSpy).toHaveBeenCalled();
});

内容的提问来源于stack exchange,提问作者Samuel Thompson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:34:01