RXJS TestScheduler未触发Observable,Angular Jasmine单元测试断言失败
问题:TestScheduler测试BehaviorSubject时setter未被调用
我在学习用TestScheduler做RXJS Marble测试,测试BehaviorSubject的getter和setter时遇到错误:Expected spy setVersatilityRowOptions to have been called with:[ VersatilityHeaderRowOptions({ onlyShowCertifiedOperators: true, showOffTeamOperators: false })] but it was never called.
相关代码
服务函数
setVersatilityRowOptions(versatilityRowOptions: VersatilityHeaderRowOptions): void { this._versatilityRowOptions.next(versatilityRowOptions); } getVersatilityRowOptions(): Observable<VersatilityHeaderRowOptions> { return this.$versatilityRowOptions; }
待测试组件函数
updateVersatility(): void { this.versatilityApiService.getVersatilityRowOptions() .subscribe(versatilityRowOptions => { this.versatilityApiService.setVersatilityRowOptions(versatilityRowOptions); }); }
测试文件(已完成Mock和服务注入)
const testScheduler = new TestScheduler((actual, expected) => { expect(actual).toEqual(expected); }); describe('updateVersatility()', () => { it('should update behavior subject', () => { testScheduler.run(({cold}) => { const source1$ = cold('--(x|)', {x: mockVersatilityHeaderRowOptions}); spyOn(component['versatilityApiService'],'getVersatilityRowOptions') .and.returnValue(source1$); spyOn(component['versatilityApiService'], 'setVersatilityRowOptions'); component.updateVersatility(); getTestScheduler().flush(); fixture.detectChanges(); expect(component['versatilityApiService'].getVersatilityRowOptions) .toHaveBeenCalled(); expect(component['versatilityApiService'].setVersatilityRowOptions) .toHaveBeenCalledWith(mockVersatilityHeaderRowOptions); });
问题原因及修复方案
核心问题
testScheduler.run()回调内部创建的冷Observable,其调度器是当前TestScheduler实例,但你调用的getTestScheduler().flush()是全局TestScheduler,两者不是同一个实例,导致Observable事件未触发,subscribe回调不执行,setter自然没被调用。另外,testScheduler.run()内部会自动管理时间推进,无需手动调用flush。
修复后的测试代码
const testScheduler = new TestScheduler((actual, expected) => { expect(actual).toEqual(expected); }); describe('updateVersatility()', () => { it('should update behavior subject', () => { testScheduler.run(({cold}) => { const source1$ = cold('--(x|)', {x: mockVersatilityHeaderRowOptions}); spyOn(component['versatilityApiService'],'getVersatilityRowOptions') .and.returnValue(source1$); const setSpy = spyOn(component['versatilityApiService'], 'setVersatilityRowOptions'); component.updateVersatility(); fixture.detectChanges(); expect(component['versatilityApiService'].getVersatilityRowOptions).toHaveBeenCalled(); expect(setSpy).toHaveBeenCalledWith(mockVersatilityHeaderRowOptions); }); }); });
额外优化点
- 将setter的spy赋值给变量,让断言代码更简洁
- 若Angular版本支持,可结合
fakeAsync/tick进行Marble测试,确保调度器实例统一
内容的提问来源于stack exchange,提问作者Brock Bergelin
相关产品推荐
相关产品推荐

