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

单元测试中如何覆盖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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 18:54:02