Jasmine单元测试spyOn.callThrough()报spy未被调用问题
为什么
spyOn().and.callThrough()不会自动执行被监听的方法 spyOn 从设计上就只是个方法调用记录器,从来没有自动触发方法的功能,不管加不加callThrough配置都一样。
它的工作流程是:
- 找到你传入的对象上的目标方法,把原方法替换成一个带记录功能的包装函数
- 后续所有对这个方法的调用,都会被这个包装函数捕获,记录调用次数、入参、返回值等信息
- 不同的配置只决定「捕获到调用之后,要不要执行原方法的逻辑」:
- 默认配置(不写callThrough):拦截调用,不执行原方法逻辑,默认返回
undefined - 配置
.and.callThrough():不拦截调用,记录完信息之后,正常执行原方法的原有逻辑 - 其他配置比如
.and.returnValue()、.and.callFake()都是同理,决定捕获到调用之后的行为,前提永远是你得真的去调用这个被监听的方法,spy才能捕获到记录
- 默认配置(不写callThrough):拦截调用,不执行原方法逻辑,默认返回
你最开始的测试报错原因非常直接:你只给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
相关产品推荐
相关产品推荐

