Angular测试中Jasmine Cold Marble结合toPromise导致await无限等待的问题排查
为什么Angular测试中
await component.getDebt()会超时? 这个问题我之前也踩过坑,核心原因在于你用jasmine-marbles的cold()创建了一个虚拟时间Observable,但测试里没有触发虚拟时间的推进,导致Observable永远不会完成,对应的toPromise()也就永远不会resolve,await自然就一直挂着直到超时。
具体原因拆解
cold('--x|', {x:1})创建的Observable是基于RxJS的虚拟时间调度器运行的,它不会像普通Observable那样立即执行事件。这里的--x|表示“等待2个时间单位后发出值x,然后完成”,但如果没人告诉测试框架“推进时间走完这2个单位”,这个Observable就会一直处于等待状态,toPromise()也就永远得不到resolve信号,await component.getDebt()就卡在这里超时了。
两种解决方法
方法1:推进虚拟时间(保留jasmine-marbles的用法)
如果你需要测试时间相关的逻辑(比如延迟、时序),可以通过TestScheduler来推进虚拟时间,让Observable完成:
import { TestScheduler } from 'rxjs/testing'; // ... 其他导入不变 it('should call logGetDebtAttempt after getting debt', async () => { const testScheduler = new TestScheduler((actual, expected) => { expect(actual).toEqual(expected); }); spyOn(component, 'logGetDebtAttempt').and.callThrough(); await testScheduler.run(async () => { await component.getDebt(); expect(component.logGetDebtAttempt).toHaveBeenCalled(); }); });
或者如果你用的是旧版jasmine-marbles,可以直接调用flush()来触发所有虚拟时间的事件:
import { flush } from 'jasmine-marbles'; // ... 其他代码不变 it('should call logGetDebtAttempt after getting debt', (done) => { spyOn(component, 'logGetDebtAttempt').and.callThrough(); component.getDebt().then(() => { expect(component.logGetDebtAttempt).toHaveBeenCalled(); done(); }); flush(); // 一次性推进所有虚拟时间,让Observable完成 });
方法2:改用同步Observable(无需虚拟时间)
如果你的测试不需要验证时间逻辑,完全可以直接返回一个同步完成的Observable,这样toPromise()会立刻resolve:
首先修改你的服务stub:
import { of } from 'rxjs'; const chartServiceStub = { getNationalDebt(): Observable<Number> { return of(1); // 同步发出值并立即完成 } };
然后你的原测试代码就可以正常运行了,不需要额外处理时间,await component.getDebt()会很快完成,断言也能正常执行。
内容的提问来源于stack exchange,提问作者sinaC
相关产品推荐
相关产品推荐

