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

Jest SpyOn在NgRx组件Store Effect中监听服务方法失效问题

测试NgRx组件Store的Effect方法loadReviewByRouteId时断言失败的解决方法

我在测试NgRx组件Store的effect方法loadReviewByRouteId时遇到异常:运行测试时控制台输出了here!,说明getReviewById方法确实被执行了,但断言却失败,提示该方法调用次数为0。

相关Effect代码

readonly loadReviewByRouteId = this.effect(
  (): Observable<EmployeeReview> =>
    this.route.params.pipe(
      switchMap(({id}) => this.reviewService.getReviewById(id)),
      tap((review) => this.setEmployeeReview(review))
    )
)

测试配置

const mockReviewService = {
   getReviewById() {
      console.log('here!')
   }
}

TestBed.configureTestingModule({
  imports: [RouterTestingModule.withRoutes([{path: '**', redirectTo: ''}])],
  providers: [
    ReviewDetailsStore,
    {
      provide: ReviewService,
      useValue: mockReviewService
    },
    {
      provide: ActivatedRoute,
      useValue: {
        params: of({id: 123})
      }
    }
  ]
})

测试用例

it('should call the review service with the route param id', () => {
  const getReviewServiceSpy = jest.spyOn(reviewService, 'getReviewById')
  reviewDetailsStore.loadReviewByRouteId()
  expect(getReviewServiceSpy).toHaveBeenCalled()
})

错误信息

expect(jest.fn()).toHaveBeenCalledWith(...expected)

Expected: called with 0 arguments

Number of calls: 0


解决方法

1. 确保Spy挂载到正确的实例

你当前的mockReviewService是字面量对象,jest.spyOn需要挂载到TestBed注入的实际服务实例上,而非原始mock对象。修改测试用例,从TestBed中获取服务实例后再挂载Spy:

it('should call the review service with the route param id', () => {
  const reviewService = TestBed.inject(ReviewService);
  const getReviewServiceSpy = jest.spyOn(reviewService, 'getReviewById');
  reviewDetailsStore.loadReviewByRouteId();
  expect(getReviewServiceSpy).toHaveBeenCalledWith(123);
})

2. 处理Observable的异步特性

NgRx Component Store的Effect返回Observable,属于异步操作。若不等待异步流程完成,断言会在代码执行前触发。可以用Angular测试工具tick()结合async/await处理:

it('should call the review service with the route param id', async () => {
  const reviewService = TestBed.inject(ReviewService);
  const getReviewServiceSpy = jest.spyOn(reviewService, 'getReviewById')
    .mockReturnValue(of({} as EmployeeReview));

  reviewDetailsStore.loadReviewByRouteId();
  await tick(); // 触发所有异步操作
  expect(getReviewServiceSpy).toHaveBeenCalledWith(123);
})

3. 修复mock方法的返回值

原getReviewById方法未返回值,但Effect的switchMap期望它返回Observable,这会导致管道中断,影响异步流程。给mock方法添加Observable返回值:

const mockReviewService = {
   getReviewById(id: number) {
      console.log('here!')
      return of({} as EmployeeReview); // 返回模拟的EmployeeReview Observable
   }
}

内容的提问来源于stack exchange,提问作者user3817370

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 14:20:40