使用Pipe时Jest测试失败,添加Subscription后测试通过
问题解决:NgRx Action测试中patchState断言失败的处理
问题根源
Observable是冷流,只有被订阅时才会执行内部逻辑。运行时NgRx框架会自动订阅Action方法返回的Observable,所以tap里的patchState能正常执行;但单元测试中直接调用updateData方法,没有触发订阅,导致tap内的代码完全没运行,断言自然失败。
合理解决方案(无需在Action中手动订阅)
方法1:用RxJS的firstValueFrom结合async/await(推荐)
RxJS 7+提供的firstValueFrom会自动订阅Observable,获取第一个值后完成并取消订阅,完美适配测试场景:
import { firstValueFrom } from 'rxjs'; // ... it('should update data', async () => { await firstValueFrom(state.updateData(stub.ctx)); expect(stub.MyApiService.getSomeData).toHaveBeenCalledTimes(1); expect(stub.ctx.patchState).toHaveBeenCalledTimes(1); // 断言通过 });
方法2:手动订阅+jest的done回调
适合需要监听流完成事件的场景:
it('should update data', (done) => { const actionObservable = state.updateData(stub.ctx); actionObservable.subscribe({ complete: () => { expect(stub.MyApiService.getSomeData).toHaveBeenCalledTimes(1); expect(stub.ctx.patchState).toHaveBeenCalledTimes(1); done(); // 通知jest测试完成 } }); });
方法3:简单手动订阅后取消
针对同步mock的Observable(比如用of创建的流),订阅后会立即执行逻辑,之后直接取消订阅即可:
it('should update data', () => { const subscription = state.updateData(stub.ctx).subscribe(); expect(stub.MyApiService.getSomeData).toHaveBeenCalledTimes(1); expect(stub.ctx.patchState).toHaveBeenCalledTimes(1); subscription.unsubscribe(); // 清理订阅 });
注意事项
绝对不要在Action方法内手动调用subscribe——NgRx框架会自动处理Action返回Observable的订阅与取消操作,手动订阅不仅冗余,还可能引发内存泄漏问题,违背NgRx的设计规范。
内容的提问来源于stack exchange,提问作者Remi
相关产品推荐
相关产品推荐

