Angular中Jasmine如何Spy对象内方法?测试报错解决
我在CapacitorManager服务中实现了backButtonHandler方法,逻辑是当dataService.getItem返回的activeModalInstance(NgbModal实例)存在时调用其close方法,否则调用location.back()。我已经mock了dataService.getItem让它返回带close方法的对象,但测试时抛出"Expected a spy, but got Function."错误,怎么才能通过这个测试?
服务代码
backButtonHandler() { return () => { const activeModalInstance = this.dataService.getItem(Constants.activeModalInstanceKey); const doesActiveModalExist = activeModalInstance !== -1; if (doesActiveModalExist) { activeModalInstance.close(); } else { this.location.back(); } } }
原测试代码
fdescribe('CapacitorManager', () => { let service: CapacitorManager; let dataService: DataService; let location: Location; let dataServiceStub: Partial<DataService>; beforeEach(async(() => { dataServiceStub = { getItem: () => { return { close: () => { } } } } TestBed.configureTestingModule({ providers: [ { provide: Location, useClass: SpyLocation }, { provide: DataService, useValue: dataServiceStub }, CapacitorManager ], imports: [ RouterTestingModule ] }).compileComponents().then(() => { service = TestBed.inject(CapacitorManager); dataService = TestBed.inject(DataService); location = TestBed.inject(Location); }); })); afterEach(() => { service = null; }); it('should create the component', () => { expect(service).toBeTruthy(); }); it('should backButtonHandler call activeModal in case activeModalInstance exists', () => { const activeModalInstance = dataService.getItem("activeModal"); spyOn(activeModalInstance, "close"); service.backButtonHandler(); expect(activeModalInstance.close).toHaveBeenCalled(); expect(location.back).not.toHaveBeenCalled(); }); });
解决方法
问题根源
测试中存在两个核心问题:
- 你的dataService stub每次调用
getItem都会返回一个新的对象,导致你提前创建spy的实例和backButtonHandler内部调用getItem拿到的实例不是同一个,断言时自然找不到spy。 backButtonHandler返回的是一个函数,但测试里只调用了backButtonHandler(),没有执行这个返回的函数,内部逻辑根本没跑。
修复后的测试代码
fdescribe('CapacitorManager', () => { let service: CapacitorManager; let dataService: DataService; let location: SpyLocation; let mockModalInstance: { close: jasmine.Spy }; beforeEach(async(() => { // 提前创建固定的mock实例,并给close方法创建spy mockModalInstance = { close: jasmine.createSpy('close') }; const dataServiceStub: Partial<DataService> = { // 让getItem始终返回同一个mock实例 getItem: jasmine.createSpy('getItem').and.returnValue(mockModalInstance) }; TestBed.configureTestingModule({ providers: [ { provide: Location, useClass: SpyLocation }, { provide: DataService, useValue: dataServiceStub }, CapacitorManager ], imports: [ RouterTestingModule ] }).compileComponents().then(() => { service = TestBed.inject(CapacitorManager); dataService = TestBed.inject(DataService); location = TestBed.inject(Location) as SpyLocation; }); })); afterEach(() => { service = null; }); it('should create the service', () => { expect(service).toBeTruthy(); }); it('should call activeModal.close() when activeModalInstance exists', () => { // 获取backButtonHandler返回的函数并执行它 const handler = service.backButtonHandler(); handler(); // 断言close方法被调用 expect(mockModalInstance.close).toHaveBeenCalled(); // 断言location.back没有被调用 expect(location.back).not.toHaveBeenCalled(); // 可选:断言dataService.getItem被正确调用 expect(dataService.getItem).toHaveBeenCalledWith(Constants.activeModalInstanceKey); }); });
额外注意事项
- 确保测试中调用
getItem的参数和服务里的Constants.activeModalInstanceKey一致,避免因为key不匹配导致逻辑走else分支。 - 如果需要测试else分支(即没有modal实例时调用location.back),可以修改dataService stub的返回值为
-1,然后断言location.back被调用。
内容的提问来源于stack exchange,提问作者Timuçin Çiçek
相关产品推荐
相关产品推荐

