如何测试组件中结合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
相关产品推荐
相关产品推荐

