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

Jasmine单元测试spyOn.callThrough()报spy未被调用问题

为什么spyOn().and.callThrough()不会自动执行被监听的方法

spyOn 从设计上就只是个方法调用记录器,从来没有自动触发方法的功能,不管加不加callThrough配置都一样。

它的工作流程是:

  • 找到你传入的对象上的目标方法,把原方法替换成一个带记录功能的包装函数
  • 后续所有对这个方法的调用,都会被这个包装函数捕获,记录调用次数、入参、返回值等信息
  • 不同的配置只决定「捕获到调用之后,要不要执行原方法的逻辑」:
    • 默认配置(不写callThrough):拦截调用,不执行原方法逻辑,默认返回undefined
    • 配置.and.callThrough():不拦截调用,记录完信息之后,正常执行原方法的原有逻辑
    • 其他配置比如.and.returnValue()、.and.callFake()都是同理,决定捕获到调用之后的行为,前提永远是你得真的去调用这个被监听的方法,spy才能捕获到记录

你最开始的测试报错原因非常直接:你只给duplicateObject套了监听壳,全程没有任何地方真的调用这个方法,spy里的调用记录是空的,toHaveBeenCalled()自然会失败。你后面加的component.duplicateObject(newObj)就是真正触发方法执行的步骤,少了这行,spy根本等不到调用信号。

顺便提两个你业务代码里的笔误,之前没报错是因为测试根本没真跑方法逻辑,真跑的时候会直接抛错:

  • duplicateObject 方法里删除属性的语句写的是delete item.id,但方法入参名是obj,item这个变量根本没定义,应该改成delete obj.id
  • 调用新建方法的语句拼错了方法名:this.creteNewObject(obj),你定义的方法名是createNewObject,少了个字母a,执行时会报方法不存在。

另外你最初的测试写法其实没测到真正的业务逻辑——你给被测方法duplicateObject打spy,哪怕断言通过,也只能证明「你手动调了一次duplicateObject」,根本验证不了「duplicateObject内部会移除id、调用createNewObject」的核心逻辑。正确的测试写法应该把spy打在依赖的createNewObject方法上,示例如下:

it('should remove id property and invoke createNewObject when duplicateObject is called', () => {
  // 给内部依赖的方法打spy,验证它被正确调用
  const createNewObjectSpy = spyOn(component, 'createNewObject');
  const mockObj = { id: 1001, content: 'test content' };

  // 执行被测方法
  component.duplicateObject(mockObj);

  // 断言逻辑符合预期
  expect(mockObj.id).toBeUndefined(); // id属性被移除
  expect(createNewObjectSpy).toHaveBeenCalledOnceWith({ content: 'test content' }); // 处理后的对象被传入createNewObject
})

内容的提问来源于stack exchange,提问作者Michał Sawicki

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 23:33:26