Angular单元测试报错:Expected spy get to have been called排查求助
Angular Component1单元测试Spy未触发问题
错误现象
编写Component1的单元测试时,两个测试用例均返回相同错误:
Error: Expected spy get to have been called. at <Jasmine> at UserContext.apply (src/app/features/home/home.component.spec.ts:70:37) at _ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:409:30) at ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/zone-testing.js:303:43)
组件代码明确调用了service2.get(0),但测试中对service1.get的Spy始终未触发,尝试多种Spy创建方式均无效。
单元测试代码
const mockActiveService1 = { ....... } const mockNotActiveService1 = { ....... } describe('Component1 ', () => { let component: Component1 ; let fixture: ComponentFixture<Component1>; beforeEach(async () => { TestBed.configureTestingModule({ declarations: [Component1], providers: [ { provide: Service1, useValue: mockActiveService1 } ], imports: [HttpClientTestingModule, ToastrModule.forRoot()] }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(Component1); component = fixture.componentInstance; service1 = TestBed.inject(Service1); // 修正原代码的TestBeb拼写错误 }); it('should load the current dataService2', async () => { spyOn(service1, 'get').and.returnValue(of(mockActiveService1)); fixture.detectChanges(); await component.ngOnInit(); expect(service1.get).toHaveBeenCalled(); expect(component.dataService2).toEqual(mockActiveService1); expect(component.showCountdown).toBeFalse(); expect(component.loading).toBeFalse(); }); it('should show info message and navigate to default route when there is no active dataService2', fakeAsync(() => { const toastrServiceSpy = spyOn(toastrServiceMock, 'info'); const routerSpy = spyOn(routerMock, 'navigate'); const getSpy = spyOn(service1, 'get').and.callThrough().and.returnValue(of(mockNotActiveService1)); fixture.detectChanges(); component.ngOnInit(); tick(); expect(getSpy).toHaveBeenCalled(); expect(toastrServiceSpy).toHaveBeenCalledWith('no active dataService2', 'Info!'); expect(routerSpy).toHaveBeenCalledWith(['/default']); }));
组件核心代码
ngOnInit(): void { this.loading = true; this.showCountdown = false; this.loadCurrentdataService2(); } loadCurrentdataService2(): void { this.service2.get(0).subscribe(results => { if (results) { this.dataService2= results; if (this.dataService2 && this.dataService2.isActive) { ...............
调试尝试
在测试中添加日志查看调用情况:
it('should load the current dataService2', fakeAsync(() => { spyOn(service1, 'get').and.callThrough(); fixture.detectChanges(); component.ngOnInit(); tick(); console.log('service1.get.calls', (service1.get as any).calls); expect(service1.get).toHaveBeenCalled(); expect(component.dataService2).toEqual(mockActiveService1); expect(component.showCountdown).toBeFalse(); expect(component.loading).toBeFalse(); }));
输出结果:
LOG: 'service1.get.calls', CallTracker{track: function(context) { ... }, any: function() { ... }, count: function() { ... }, argsFor: function(index) { ... }, thisFor: function(index) { ... }, all: function() { ... }, allArgs: function() { ... }, first: function() { ... }, mostRecent: function() { ... }, reset: function() { ... }, saveArgumentsByValue: function() { ... }}
同时尝试用callFake创建Spy:
spyOn(service1, 'get').and.callFake(() => { return of(mockActiveService1); });
问题依旧。
排查思路及解决建议
- 核对服务依赖匹配:组件调用的是
service2.get,但测试Spy的是service1.get,确认Service1与Service2是否为同一服务,或组件是否注入了错误的服务实例。若组件依赖Service2,测试却提供并SpyService1,必然无法触发。 - 修正测试拼写错误:原测试中
service1 = TestBeb.inject(Service1);存在拼写错误,应为TestBed.inject,该错误会导致service1未正确获取实例,Spy无效。 - 调整Spy创建时机:确保在
component.ngOnInit()或fixture.detectChanges()之前创建Spy,否则组件初始化时调用的服务方法无法被Spy捕获。比如第一个测试先调用fixture.detectChanges()再创建Spy,此时ngOnInit已执行,Spy不会生效。 - 检查mock对象方法定义:确认
mockActiveService1和mockNotActiveService1是否定义了get方法,若mock对象无该方法,spyOn(service1, 'get')会失败,导致Spy未正确创建。 - 修正异步处理逻辑:第一个测试使用
async/await,但ngOnInit内的loadCurrentdataService2是订阅Observable,await component.ngOnInit()不会等待订阅完成,建议改用fakeAsync/tick或async/fixture.whenStable()处理异步操作。
内容的提问来源于stack exchange,提问作者Kenzo_Gilead
相关产品推荐
相关产品推荐

