单元测试中如何覆盖entityAdapter.getSelectors创建的NGRX选择器?
可行测试方案
核心问题是类型推导不匹配:createEntityAdapter 返回的 selectAll 类型和 MockStore.overrideSelector 期望的 MemoizedSelector 类型在嵌套状态下推导不一致,运行时逻辑完全兼容,只需要在测试侧做类型断言即可,无需修改任何业务代码。
修改后的测试核心逻辑
import { MemoizedSelector } from '@ngrx/store'; describe('books$', () => { it('books$ should have the same value as what booksSelector gives', () => { const testBooks: Book[] = [testBook]; const expected = cold('a', { a: testBooks }); // 仅做类型断言,无运行时开销,完全匹配overrideSelector的类型要求 const mockBooksSelector = store.overrideSelector( booksSelector as MemoizedSelector<RootState, Book[]>, testBooks ); expect(spectator.component.books$).toBeObservable(expected); }); it('should update value when selector result changes', () => { const firstTestBooks: Book[] = [testBook]; const secondTestBooks: Book[] = [ testBook, { bookID: 'bookid_2', whatever: 'test2' } ]; const expected = cold('a-b', { a: firstTestBooks, b: secondTestBooks }); const mockBooksSelector = store.overrideSelector( booksSelector as MemoizedSelector<RootState, Book[]>, firstTestBooks ); // 可以正常使用setResult方法更新返回值 mockBooksSelector.setResult(secondTestBooks); store.refreshState(); expect(spectator.component.books$).toBeObservable(expected); }); })
可选优化(非必须)
如果不想每次写类型断言,可以在测试文件顶部统一声明转换后的selector类型:
const typedBooksSelector = booksSelector as MemoizedSelector<RootState, Book[]>; // 后续所有测试直接用typedBooksSelector即可
该方案完全符合你的要求:
- 未修改任何业务代码,仅测试侧加了类型断言
- 不需要手动修改根state的值,直接通过overrideSelector控制返回结果
- 不需要验证store.pipe的传参,直接验证books$的实际输出值
- 支持后续使用setResult方法更新selector返回值处理多场景测试
内容的提问来源于stack exchange,提问作者Burnee
相关产品推荐
相关产品推荐

