Angular 16 Jasmine测试报错:预期Spy被调用但未触发
Angular 16 Jasmine单元测试问题:ConfirmationService.confirm未被调用
我在Angular 16项目中用Jasmine编写单元测试时,多个组件遇到同一问题:测试关闭侧边栏的提示逻辑时,测试文件始终报错:Error: Expected spy ConfirmationService.confirm to have been called with [ Object(...) ] but it was never called。尝试过使用async/fakeAsync都无效,且相同逻辑在其他组件测试正常,代码调用也未发现差异,求解决办法。
SPEC.TS代码
describe('EditDeclarationComponent', () => { let component: EditDeclarationComponent; let fixture: ComponentFixture<EditDeclarationComponent>; let installationServiceSpy: jasmine.SpyObj<InstallationService>; let confirmationServiceSpy: jasmine.SpyObj<ConfirmationService>; let declarationServiceSpy: jasmine.SpyObj<DeclarationService>; beforeEach(async () => { installationServiceSpy = jasmine.createSpyObj('InstallationService', [ 'getInstallationsType', 'existInstallationNPM', 'saveInstalacion', ]); confirmationServiceSpy = jasmine.createSpyObj('ConfirmationService', [ 'confirm', ]); declarationServiceSpy = jasmine.createSpyObj('DeclarationService', [ 'updateDeclaration', ]); await TestBed.configureTestingModule({ declarations: [ EditDeclarationComponent, NumberPipe, SgIconComponent, SgFormAddDeclarationDestinationComponent, SgButtonComponent, ], providers: [ { provide: InstallationService, useValue: installationServiceSpy }, MessageService, { provide: ConfirmationService, useValue: confirmationServiceSpy }, { provide: DeclarationService, useValue: declarationServiceSpy }, ], imports: [ HttpClientModule, DialogModule, DividerModule, ConfirmDialogModule, ToastModule, AutoCompleteModule, InputTextModule, FormsModule, ReactiveFormsModule, ], }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(EditDeclarationComponent); component = fixture.componentInstance; installationServiceSpy = TestBed.inject( InstallationService ) as jasmine.SpyObj<InstallationService>; confirmationServiceSpy = TestBed.inject( ConfirmationService ) as jasmine.SpyObj<ConfirmationService>; declarationServiceSpy = TestBed.inject( DeclarationService ) as jasmine.SpyObj<DeclarationService>; installationServiceSpy.getInstallationsType.and.returnValue(of(response)); fixture.detectChanges(); });
测试用例代码
it('should set instalacionNew and dialogNewInstalacion correctly when accepted', () => { const mockConfirmationResponse: any = { accept: (callback: Function) => { callback(); }, }; confirmationServiceSpy.confirm.and.callFake((options: any) => options.accept(mockConfirmationResponse.accept) ); component.onCancelNewInstallation(); expect(component.instalacionNew).toEqual({} as Installation); expect(component.dialogNewInstalacion).toBe(false); expect(confirmationServiceSpy.confirm).toHaveBeenCalledWith({ message: 'Are you sure about exiting the reinstall window?', header: 'Exit new installation window?', icon: 'pi pi-info-circle', acceptLabel: 'Yes', rejectLabel: 'No', accept: jasmine.any(Function), }); });
组件TS代码
instalacionNew: Installation = {} as Installation; dialogNewInstalacion: boolean = false; // Installation是一个包含对象的model.ts文件 constructor( private confirmationService: ConfirmationService, private declarationService: DeclarationService, private messageService: MessageService, private cd: ChangeDetectorRef, private installationService: InstallationService ) {} onCancelNewInstallation() { this.confirmationService.confirm({ message: 'Are you sure about exiting the reinstall window?', header: 'Exit new installation window?', icon: 'pi pi-info-circle', acceptLabel: 'Yes', rejectLabel: 'No', accept: () => { this.instalacionNew = {} as Installation; this.dialogNewInstalacion = false; }, }); }
问题排查与解决办法
1. 修复Spy对象的重复赋值问题
你在第一个beforeEach(async)中创建了confirmationServiceSpy,但在第二个beforeEach里又通过TestBed.inject重新赋值了一次,可能导致Spy没有正确关联到组件实例。移除第二个beforeEach中的Spy重新注入代码:
// 修改后的第二个beforeEach beforeEach(() => { fixture = TestBed.createComponent(EditDeclarationComponent); component = fixture.componentInstance; // 移除此处三个Spy的重新注入代码 installationServiceSpy.getInstallationsType.and.returnValue(of(response)); fixture.detectChanges(); });
2. 调整测试中Spy的调用逻辑
当前callFake的写法有误,组件的accept回调不需要参数,直接执行即可。同时用jasmine.objectContaining代替严格对象匹配,避免因引用或额外属性导致匹配失败:
it('should set instalacionNew and dialogNewInstalacion correctly when accepted', () => { // 直接模拟confirm方法,执行传入的accept回调 confirmationServiceSpy.confirm.and.callFake((options: any) => { options.accept(); }); component.onCancelNewInstallation(); expect(component.instalacionNew).toEqual({} as Installation); expect(component.dialogNewInstalacion).toBe(false); expect(confirmationServiceSpy.confirm).toHaveBeenCalledWith(jasmine.objectContaining({ message: 'Are you sure about exiting the reinstall window?', header: 'Exit new installation window?', icon: 'pi pi-info-circle', acceptLabel: 'Yes', rejectLabel: 'No', accept: jasmine.any(Function) })); });
3. 排查组件生命周期的Service覆盖问题
检查组件的ngOnInit或其他生命周期钩子中是否重新实例化了ConfirmationService,如果有会导致Spy失效。
4. 按需添加变更检测(可选)
如果测试中发现属性未更新,可在调用component.onCancelNewInstallation()后添加fixture.detectChanges()触发变更检测。
内容的提问来源于stack exchange,提问作者marocena26
相关产品推荐
相关产品推荐

