测试时无法触发forkJoin的subscribe代码块的解决方法
问题分析与解决
核心原因
测试中forkJoin的subscribe回调未执行,是因为RxJS的异步操作需要测试环境主动触发异步队列刷新——Jest默认不会自动等待RxJS的异步流完成,即便用了async(),也需要明确等待异步操作结束。
另外注意组件代码里的笔误:this.value = res.summary;应该是res.summary2,否则会取到不存在的属性,导致逻辑出错。
解决方案
方法1:使用fakeAsync + tick()(推荐)
Angular测试工具提供的fakeAsync可以模拟时间环境,tick()会立即刷新所有待处理的异步任务(包括RxJS的Observable流),确保subscribe回调执行:
it('setup()', fakeAsync(() => { const expectedData = mockData; // 模拟service返回完成的Observable const summary1Spy = jest.spyOn(service1, 'getSummary').mockReturnValue(of(expectedData)); const summary2Spy = jest.spyOn(service2, 'getSummary').mockReturnValue(of({/* 模拟返回值 */})); component.setup(); tick(); // 触发所有RxJS异步操作,执行subscribe回调 fixture.detectChanges(); // 若需要更新组件视图,可添加此句 // 断言逻辑 expect(summary1Spy).toHaveBeenCalled(); expect(summary2Spy).toHaveBeenCalled(); expect(component.value).toEqual(/* 预期的summary2返回值 */); }));
方法2:手动等待Observable完成
如果不想用fakeAsync,可以修改组件的setup方法返回forkJoin的Observable,再在测试中等待其完成:
首先修改组件代码:
setup(): Observable<any> { const observables$ = { summary1: this.service1.getSummary(data), summary2: this.service2.getSummary() }; const forkJoin$ = forkJoin(observables$); forkJoin$.subscribe((res) => { if(res.summary1) { // 操作逻辑 } if(res.summary2) { this.value = res.summary2; // 修正笔误 } }); return forkJoin$; }
然后修改测试代码:
it('setup()', async() => { const expectedData = mockData; const summary1Spy = jest.spyOn(service1, 'getSummary').mockReturnValue(of(expectedData)); const summary2Spy = jest.spyOn(service2, 'getSummary').mockReturnValue(of({/* 模拟值 */})); await lastValueFrom(component.setup()); fixture.detectChanges(); expect(summary1Spy).toHaveBeenCalled(); expect(summary2Spy).toHaveBeenCalled(); expect(component.value).toEqual(/* 预期值 */); });
额外注意点
forkJoin要求所有内部Observable都完成才会发射值,你的mock返回of()是没问题的(因为of()会立即完成);如果mock的是不会完成的Observable(比如BehaviorSubject),需要手动调用complete()。- 组件代码中的
res.summary是明显笔误,必须修正为res.summary2,否则即便进入回调,this.value也会被赋值为undefined。
内容的提问来源于stack exchange,提问作者Aastha B
相关产品推荐
相关产品推荐

