如何用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
相关产品推荐
相关产品推荐

