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

如何测试返回empty()的@ngrx/effects 4.1.1 Effect?

解决Ngrx Effects返回empty()的单元测试问题

我之前也踩过Ngrx Effects 4.x版本测试空Observable Effect的坑,结合你的代码和报错信息,给你梳理下问题原因和解决办法:

问题根源

你看到的报错是因为:

  1. 你的Effect没有明确设置dispatch: false,Ngrx默认会期望这个Effect分发Action,但你返回的empty()是直接完成的Observable,没有任何Action输出,导致测试匹配失败。
  2. 即使你后来加了dispatch: false,可能测试写法上还有小问题,没完全适配Ngrx 4.x的测试逻辑。

分步解决

1. 给Effect加上dispatch: false元数据

这是核心第一步!因为你的Effect只执行副作用(调用dialogsService.showDialog),不分发任何Action,必须显式告诉Ngrx这个配置:

@Effect({ dispatch: false }) // 加上这个配置!
showDialog$: Observable<Action> = this
  .actions$
  .ofType(ActionTypes.DIALOG_SHOW)
  .map((action: DialogAction) => action.payload)
  .switchMap(payload => {
    this.dialogsService.showDialog(payload.className);
    return empty();
  });

2. 调整测试代码,兼顾副作用验证和空Observable匹配

测试这类Effect的重点应该是验证副作用是否执行,同时确认没有Action被分发。你可以这样修改测试:

describe('DialogEffects', () => {
  let effects: DialogEffects;
  let actions: Observable<any>;
  const mockDialogService = { showDialog: sinon.stub() };

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        DialogEffects,
        provideMockActions(() => actions),
        { provide: DialogsService, useValue: mockDialogService }
      ]
    });
    effects = TestBed.get(DialogEffects);
  });

  describe('showDialog$', () => {
    it('should call dialogService.showDialog and not dispatch any actions', () => {
      const dialogName = 'testDialog';
      const action = showDialog(dialogName);
      // 模拟Action流
      actions = hot('--a-', { a: action });

      // 订阅Effect触发执行,验证服务方法调用
      effects.showDialog$.subscribe({
        complete: () => {
          expect(mockDialogService.showDialog.calledWith(dialogName)).toBe(true);
        }
      });

      // 断言Effect返回空Observable(无任何Action输出)
      expect(effects.showDialog$).toBeObservable(empty());
    });
  });
});

如果你还是想用hot/cold的匹配方式,也可以这样写,同样能通过测试:

it('should return an empty observable and trigger showDialog', () => {
  const dialogName = 'testDialog';
  const action = showDialog(dialogName);
  actions = hot('a', { a: action });
  
  // cold('|')代表立即完成的空Observable,和empty()行为一致
  const expected = cold('|');
  
  expect(effects.showDialog$).toBeObservable(expected);
  // 重点:验证副作用执行
  expect(mockDialogService.showDialog).toHaveBeenCalledWith(dialogName);
});

关键提醒

  • 在Ngrx 4.x中,dispatch: false不是可选的,对于不输出Action的Effect必须设置,否则测试框架会一直期望有Action输出,导致匹配失败。
  • 测试这类Effect时,不要只关注空Observable的匹配,更要验证核心的副作用逻辑(比如服务方法调用),这才是这个Effect的实际作用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:34:03