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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 22:02:41