Ionic迁移至Spectator单元测试时spectator.fixture.whenStable()超时问题
Spectator测试中fixture.whenStable()超时问题解决方案
问题背景
我们正在将现有Ionic单元测试迁移至Spectator框架,测试初始化代码如下:
beforeEach(async() => { spectator = createComponent() generalConfigService = TestBed.inject(GeneralConfigService) spyOn(spectator.component.generalService, 'getGeneralConfigState').and.returnValue(of(configDataMock)) spectator.component.chartElement = chartViewInputMock.chartElement spectator.component.chartElementID = "0123456789" spectator.component.localstorageservice.setStoragedata('WXcurrentLevelInfo', { "level": "plant", "id": "64291", "timezone": { "abbr": "UTC", "offset": 0, "name": "UTC" } }) spectator.component.localstorageservice.setStoragedata('PlantID', "64291") spectator.component.localstorageservice.setStoragedata('appConfigData', loginSuccessMock.appConfigData) await spectator.component.ngOnInit(); })
执行测试用例时:
it('用户可在仪表板未达上限时复制元素',(async(done) => { let spy = spyOn(spectator.component.commonService,'alertMessage') popoverSpy.onDidDismiss.and.returnValue(Promise.resolve({data:'chartDuplicate'})); spectator.component.chartPopover('') // 需等该函数执行完成后再执行后续代码 await spectator.fixture.whenStable() /// 始终无法解析,导致单元测试超时错误 expect(popoverSpy.present).toHaveBeenCalled(); expect(alertSpy.present).toHaveBeenCalled() done() }))
核心问题:spectator.fixture.whenStable()时常无法解析,导致后续代码无法执行,最终出现超时错误:
Error: Timeout - Async function did not complete within 5000ms (set by jasmine.DEFAULT_TIMEOUT_INTERVAL)
解决办法
1. 移除async/await与done回调的混合使用
同时使用async函数和done回调会导致Jasmine异步逻辑混乱,直接改用纯async/await写法:
it('用户可在仪表板未达上限时复制元素', async() => { let spy = spyOn(spectator.component.commonService,'alertMessage') popoverSpy.onDidDismiss.and.returnValue(Promise.resolve({data:'chartDuplicate'})); spectator.component.chartPopover('') await spectator.fixture.whenStable() expect(popoverSpy.present).toHaveBeenCalled(); expect(alertSpy.present).toHaveBeenCalled() })
2. 手动触发变更检测
Spectator默认可能不会自动触发变更检测,在调用目标函数后手动执行变更检测,再等待稳定:
it('用户可在仪表板未达上限时复制元素', async() => { let spy = spyOn(spectator.component.commonService,'alertMessage') popoverSpy.onDidDismiss.and.returnValue(Promise.resolve({data:'chartDuplicate'})); spectator.component.chartPopover('') spectator.detectChanges(); // 手动触发变更检测 await spectator.fixture.whenStable() expect(popoverSpy.present).toHaveBeenCalled(); expect(alertSpy.present).toHaveBeenCalled() })
3. 确保异步依赖完全Mock
检查chartPopover内部的异步操作,确保所有依赖的异步方法都返回已解析的Promise或完成的Observable:
- 若
commonService.alertMessage包含异步逻辑,需Mock为返回Promise.resolve() - 确认popover的
present方法也被正确Mock,比如添加popoverSpy.present.and.returnValue(Promise.resolve())
4. 临时延长超时时间
若以上方法无效,可临时延长Jasmine超时时间,验证是否为异步操作本身耗时较长:
it('用户可在仪表板未达上限时复制元素', async() => { jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; // 延长至10秒 let spy = spyOn(spectator.component.commonService,'alertMessage') popoverSpy.onDidDismiss.and.returnValue(Promise.resolve({data:'chartDuplicate'})); spectator.component.chartPopover('') await spectator.fixture.whenStable() expect(popoverSpy.present).toHaveBeenCalled(); expect(alertSpy.present).toHaveBeenCalled() })
5. 使用Spectator内置的runAsync方法
用Spectator提供的runAsync方法处理异步测试,替代直接调用fixture.whenStable():
it('用户可在仪表板未达上限时复制元素', () => { let spy = spyOn(spectator.component.commonService,'alertMessage') popoverSpy.onDidDismiss.and.returnValue(Promise.resolve({data:'chartDuplicate'})); return spectator.runAsync(async () => { spectator.component.chartPopover('') await spectator.fixture.whenStable() expect(popoverSpy.present).toHaveBeenCalled(); expect(alertSpy.present).toHaveBeenCalled() }) })
内容的提问来源于stack exchange,提问作者Vaibhav Shah
相关产品推荐
相关产品推荐

