Angular12升级后Karma测试SpyOn监听私有方法内服务调用失效问题
问题成因
这个问题是测试代码执行顺序和Angular 12变更检测时机的变化共同导致的,核心原因是你在ngOnInit触发之后才对服务方法做Spy:
- Angular组件的
ngOnInit生命周期钩子会在首次变更检测执行时触发 - 你现有测试代码的执行顺序是:
- 创建组件实例
- 开启
fixture.autoDetectChanges()—— Angular 12中开启自动变更检测后会立刻执行首次变更检测,此时ngOnInit已经执行完成,doThis里调用的是原始的myService.confirm方法 - 才对
MyService.confirm做Spy模拟 —— 此时Spy生效的时机晚于方法调用,自然不会触发你写的模拟逻辑
- 你把
doThis改为public后手动调用能生效,是因为手动调用发生在Spy完成之后,自然能命中模拟逻辑。另外Angular 12中废弃了TestBed.get方法,替换为TestBed.inject,原有TestBed.get的类型推导和实例获取逻辑也有小幅变动,可能会放大这个时序问题。
修复方案
调整测试代码的执行顺序,所有Spy操作要放在触发首次变更检测之前,推荐写法如下:
beforeEach(async () => { // 1. 先完成服务的Spy,再创建组件、触发变更检测 const myService = TestBed.inject(MyService); spyOn(myService, 'confirm').and.callFake((params: Confirmation) => { params.reject(); }); // 2. 再创建组件实例 fixture = TestBed.createComponent(MyComponent); component = fixture.componentInstance; // 3. 最后开启自动变更检测/手动触发首次检测 fixture.autoDetectChanges(); await fixture.whenStable(); });
如果你的测试逻辑需要先创建组件再做Spy,也可以把开启自动变更检测的逻辑放到Spy之后:
beforeEach(async () => { fixture = TestBed.createComponent(MyComponent); component = fixture.componentInstance; // 先Spy,再开启变更检测 const myService = TestBed.inject(MyService); spyOn(myService, 'confirm').and.callFake((params: Confirmation) => { params.reject(); }); fixture.autoDetectChanges(); await fixture.whenStable(); });
内容的提问来源于stack exchange,提问作者dorcsi
相关产品推荐
相关产品推荐

