Angular 19升级后RxJS interval单元测试行为异常问题
Angular 19 升级后定时器单元测试多一次emit的原因分析
我有一个Angular 18服务,通过每分钟更新BehaviorSubject来追踪时间,简化代码如下:
private totalDurationMin = 0; private totalDurationMinutesSubject$ = new BehaviorSubject(this.totalDurationMin); public totalDurationMinutes$ = this.totalDurationMinutesSubject$.asObservable(); public clockIn(): void { this.startTimer(0); } private startTimer(startingMinutes: number): void { this.totalDurationMin = startingMinutes; this.totalDurationMinutesSubject$.next(this.totalDurationMin); this.timerSubscription = interval(60 * 1000).subscribe({ next: () => { this.totalDurationMin++; this.totalDurationMinutesSubject$.next(this.totalDurationMin); } }); }
对应的单元测试代码:
it('should update the observables and start the timer when clocking in', fakeAsync(() => { //skip initial emit, the start of the timer, and then then next 4 minute ticks service.totalDurationMinutes$.pipe(skip(6)).subscribe({ next: (val) => { console.log(`minutes: ${val}`); expect(val).toEqual(5); }, }); service.clockIn(); tick(60 * 1000 * 5); //simulate 5 minutes passing }));
该测试在Angular 18中可正常通过,但升级到Angular 19后,测试会多一次emit——断言期望5却得到6。调试日志显示Angular 18中仅输出minutes:5,Angular 19中输出minutes:5和minutes:6。目前添加take(1)可让测试通过,想了解导致该变化的原因。
原因分析
这一变化源于Angular 19配套升级的RxJS 8.x对interval在fakeAsync环境下的调度逻辑调整:
- 在Angular 18对应的RxJS 7.x中,
fakeAsync的tick(ms)只会触发到达指定时间点前的所有调度任务; - 升级到RxJS 8.x后,
interval的触发时机被调整,当调用tick(300000)(模拟5分钟)时,会刚好触发第5次计时结束后的emit,同时额外触发第6次计时的emit——因为RxJS 8的定时器调度会在时间点到达时,执行一次额外的调度循环。
具体emit次数拆解:
- BehaviorSubject初始值0(第1次)
clockIn()调用后startTimer立即发射0(第2次)- 第1分钟:发射1(第3次)
- 第2分钟:发射2(第4次)
- 第3分钟:发射3(第5次)
- 第4分钟:发射4(第6次)
- 第5分钟:发射5(第7次)
- RxJS 8中
tick(300000)额外触发第6分钟的发射6(第8次)
测试中skip(6)会跳过前6次emit,在Angular 18中仅能接收到第7次的5;而Angular 19中会同时接收到第7次的5和第8次的6,导致断言失败。添加take(1)可以限定只取第一个符合条件的emit,从而让测试通过。
内容的提问来源于stack exchange,提问作者Chris Barr
相关产品推荐
相关产品推荐

