You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 20:15:37