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

如何用Jasmine测试使用@ngrx/entity中selectAll的NgRx工厂选择器?

解答NgRx Entity选择器单元测试的三个问题

1. 如何模拟状态以测试此类选择器?

核心是匹配选择器依赖的状态层级:

  • NgRx Entity的状态必须符合EntityState<T>结构:{ ids: string[] | number[], entities: Record<string | number, T> },可以用adapter.getInitialState()生成基础空状态,或用adapter.setAll()/adapter.addOne()等方法生成带测试数据的状态。
  • 如果你用createFeatureSelector定义了feature层级的选择器(比如selectItemsState = createFeatureSelector<EntityState<Item>>('items')),那么业务选择器(比如selectAllItems)是基于这个feature切片的。测试时,传给选择器projector的参数应该是这个feature切片的状态,而非全局状态。

举个正确的模拟示例:

const adapter = createEntityAdapter<Item>();
// 生成空的Entity状态
const mockItemsState = adapter.getInitialState();
// 生成带数据的Entity状态
const mockItemsWithData = adapter.setAll(
  [{ id: 1, name: '测试项' }],
  adapter.getInitialState()
);

如果你的选择器是:

export const selectAllItems = createSelector(
  selectItemsState,
  adapter.getSelectors().selectAll
);

那么测试projector时要传入mockItemsState或mockItemsWithData,而不是{ items: mockItemsState }这类全局状态结构——后者会让投影函数拿到错误的参数,导致读取ids时报错。

2. 是否需要使用provideMockStore,若需要该如何使用及原因?

分场景判断:

  • 如果你只测试选择器的投影逻辑(即projector函数本身),不需要provideMockStore,直接调用projector传入正确的状态切片即可。
  • 如果你要测试选择器从全局状态中正确切片的逻辑(比如验证createFeatureSelector和组合选择器的链路是否正确),就需要provideMockStore——选择器本身是纯函数,但组合后的选择器链需要验证是否能从全局state中拿到正确的输入,这部分逻辑无法仅通过projector测试覆盖。

使用方法(Angular + Jasmine):

import { provideMockStore } from '@ngrx/store/testing';
import { selectAllItems } from './item.selectors';

describe('Item Selectors', () => {
  let store: Store;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        provideMockStore({
          initialState: {
            // 模拟全局state中的feature切片
            items: adapter.setAll(
              [{ id: 1, name: '测试项' }],
              adapter.getInitialState()
            )
          }
        })
      ]
    });
    store = TestBed.inject(Store);
  });

  it('should select all items from the store', (done) => {
    store.select(selectAllItems).subscribe(items => {
      expect(items.length).toBe(1);
      expect(items[0].name).toBe('测试项');
      done();
    });
  });
});

3. 此场景下能否使用projector,或有其他测试选择器的方式?

完全可以用projector,但要注意传入正确的参数类型——参数必须是选择器上游依赖返回的状态值,而非全局状态。比如前面例子中,selectAllItems的上游是selectItemsState(返回EntityState<Item>),所以projector的参数必须是EntityState<Item>类型的对象,不能是全局state。

除此之外,还有两种测试方式:

  • 单独测试adapter选择器:直接调用adapter.getSelectors().selectAll(mockItemsWithData),验证其返回结果是否符合预期,再测试业务选择器是否正确组合了这些adapter选择器。
  • 使用MockStore的overrideSelector:如果需要模拟上游选择器的返回值,可以用store.overrideSelector(selectItemsState, mockItemsWithData),再测试业务选择器的输出,这种方式适合复杂的选择器链。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 04:48:29