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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 17:20:56