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

如何测试组件中结合withLatestFrom的NGXS逻辑?

问题:NGXS组件addTodo方法单元测试断言时机问题

我有一个包含addTodo方法和todos属性的组件,调用addTodo时会从NGXS store获取所有todos并赋值给todos属性,组件代码如下:

export class AppComponent {
  @Select("todo.todos") todos$!: Observable<Todo[]>;

  addTodo() {
    const todo = { title: "Todo title" };
    this.store
      .dispatch(new TodoActions.AddTodo(todo))
      .pipe(withLatestFrom(this.todos$))
      .subscribe(([_, todos]) => {
        this.todos = todos;
        console.log(this.todos); //(1)
      });
  }
}

该逻辑在浏览器中运行正常,但编写单元测试时遇到问题,当前测试代码如下:

it("should #addTodo", (done) => {
  const todo = {title: "Todo title"};

  actions.pipe(ofActionCompleted(TodoActions.AddTodo)).subscribe(() => {
    console.log(component.todos); //(2)
    expect(component.todos).toBe(todo); //Assertion fails
    done();
  });

  component.addTodo();
});

测试时日志(2)先打印undefined,断言失败,而日志(1)随后打印,说明数据在断言完成后才完成赋值。请问该如何测试此场景?


解决方案

核心原因

测试中监听ofActionCompleted(TodoActions.AddTodo)仅能确认Action执行完成,但addTodo方法里是在dispatch返回的流结合todos$最新值后,才完成this.todos的赋值,这个时机晚于Action完成的时机,导致断言时todos还未被赋值。

方法1:用fakeAsync模拟异步流程

借助Angular测试工具的fakeAsync和tick,可以触发所有异步操作,确保todos赋值完成后再断言:

it("should #addTodo", fakeAsync(() => {
  const todo = { title: "Todo title" };

  component.addTodo();
  // 触发所有待处理的异步任务
  tick();
  // 此时this.todos已完成赋值
  expect(component.todos).toContainEqual(todo);
}));

方法2:订阅组件的todos$等待数据更新

直接监听组件的todos$ Observable,等待数据更新后执行断言:

it("should #addTodo", (done) => {
  const todo = { title: "Todo title" };

  // 订阅todos$,待数据更新后断言
  component.todos$.subscribe((todos) => {
    if (todos.length > 0) {
      expect(component.todos).toEqual(todos);
      expect(todos).toContainEqual(todo);
      done();
    }
  });

  component.addTodo();
});

方法3:让addTodo返回订阅对象,等待流完成

修改组件的addTodo方法,返回订阅实例,测试时监听订阅完成事件:

调整组件代码:

addTodo() {
  const todo = { title: "Todo title" };
  return this.store
    .dispatch(new TodoActions.AddTodo(todo))
    .pipe(withLatestFrom(this.todos$))
    .subscribe(([_, todos]) => {
      this.todos = todos;
      console.log(this.todos); //(1)
    });
}

测试代码:

it("should #addTodo", (done) => {
  const todo = { title: "Todo title" };

  component.addTodo().add(() => {
    // 订阅完成时,this.todos已赋值
    expect(component.todos).toContainEqual(todo);
    done();
  });
});

额外注意事项

  • 不要用toBe断言对象/数组:toBe是引用相等判断,这里应该用toContainEqual匹配数组中的对象,或用toEqual断言整个数组内容。
  • 确保测试环境中NGXS Store已正确初始化,todos$能正常发出数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 07:45:17