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

NgRx Effects单元测试中expect语句未执行的原因排查

NgRx Effects单元测试订阅未触发、断言不执行问题解决

问题根源

你的effect通过concatLatestFrom获取store.select(selectProjectIdFromRoute)的值,再通过filter(([_, projectId]) => !projectId)过滤流。但测试中未Mock这个选择器的返回值,默认情况下该选择器可能返回非空值,导致filter条件不满足,整个流没有任何输出——这意味着订阅回调根本不会执行,断言代码从未运行,jest自然判定测试通过(无失败断言)。

解决步骤

1. Mock Store选择器的返回值

使用NgRx官方的provideMockStore工具,在测试配置中指定selectProjectIdFromRoute选择器的返回值为null/undefined,确保filter条件放行:

import { provideMockStore } from '@ngrx/store/testing';

// 在TestBed配置中添加
TestBed.configureTestingModule({
  providers: [
    YourEffectsClass,
    provideMockStore({
      selectors: [
        {
          selector: selectProjectIdFromRoute,
          value: null // 让filter条件(!projectId)成立
        }
      ]
    }),
    MockProvider(ProjectDetailsService),
    // 其他必要依赖
  ]
});

2. 确保测试等待异步流执行

原测试的订阅回调是异步的,可能在断言执行前测试就已结束。推荐用fakeAsync+tick同步触发所有异步操作:

it("should call initialNavigateToProject if project is not opened yet", fakeAsync(() => {
  const projectId = 'test-id';
  const action = invokeJoinProjectSuccess({ projectId, activeUsers: [] });

  const serviceSpy = jest.spyOn(projectDetailsService, "initialNavigateToProject");

  actions$ = of(action);

  // 订阅effect流
  effects.navigateToProjectOnInvokeJoinProject$.subscribe();

  // 触发所有异步管道操作
  tick();

  // 直接断言,无需在订阅回调内
  expect(serviceSpy).toHaveBeenCalledTimes(1);
  expect(serviceSpy).toHaveBeenCalledWith(projectId);
}));

或者用async/await结合take(1)等待流完成:

import { take } from 'rxjs';

it("should call initialNavigateToProject if project is not opened yet", async () => {
  const projectId = 'test-id';
  const action = invokeJoinProjectSuccess({ projectId, activeUsers: [] });

  const serviceSpy = jest.spyOn(projectDetailsService, "initialNavigateToProject");

  actions$ = of(action);

  // 等待流发出第一个值后完成
  await effects.navigateToProjectOnInvokeJoinProject$.pipe(take(1)).toPromise();

  expect(serviceSpy).toHaveBeenCalledTimes(1);
  expect(serviceSpy).toHaveBeenCalledWith(projectId);
});

额外说明

为什么原来的测试即使放expect(1).toEqual(2)也会通过?因为流没有发出任何值,订阅回调从未执行,jest不会运行任何断言,所以默认判定测试通过。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 09:05:23