测试Feature Module时Root Store设置异常,Selector返回undefined
你的核心问题是测试环境下根Store的状态切片(routerReducer、user)没有被正确注册,导致对应的Selector返回undefined。这是因为测试模块中StoreModule.forRoot的配置方式有误,没有正确合并根reducers和业务模块的reducers,同时缺少路由状态的初始化配置。
1. 修正StoreModule.forRoot的配置
你的根reducers已经定义了routerReducer和user的reducer映射,测试模块中只需要将companies的reducer合并到根reducers对象中即可,无需额外嵌套combineReducers(除非fromStore.reducers本身是未合并的reducer集合):
StoreModule.forRoot({ ...fromRootStore.reducers, // 导入根reducers(包含routerReducer、user) companies: combineReducers(fromStore.reducers) // 合并业务模块的reducers })
之前的写法要么重复嵌套combineReducers,要么存在语法错误,导致根状态切片未被正确注册,Selector自然找不到对应状态。
2. 配置RouterTestingModule并初始化路由状态
测试环境中路由状态不会自动初始化,你需要给RouterTestingModule配置测试路由,并在测试前手动触发导航,确保getRouterState能获取到有效值:
首先修改RouterTestingModule的导入:
RouterTestingModule.withRoutes([ { path: 'companies/:companyId', component: CompanySummaryComponent } // 匹配组件使用的路由 ])
然后在beforeEach中初始化路由状态:
// 在compileComponents之后添加 const router = TestBed.inject(Router); await router.navigate(['/companies/test-id']); // 导航到带参数的路由,触发路由状态更新
3. 修正StoreRouterConnectingModule的调用
在NGRX 4.x中,StoreRouterConnectingModule需要调用forRoot()方法完成初始化:
StoreRouterConnectingModule.forRoot()
4. 组件构造函数的小修复(非核心,但避免潜在问题)
你的组件订阅代码中,subscribe(store)应该是分发action到Store,正确写法如下:
this.actionsSubscription = this.store.select(fromRootStore.getRouterState) .pipe(map((routerState: fromRouter.RouterReducerState) => new company.Select(routerState.state.params.companyId))) .subscribe(action => this.store.dispatch(action)); // 分发action
beforeEach代码 beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ RouterTestingModule.withRoutes([ { path: 'companies/:companyId', component: CompanySummaryComponent } ]), StoreModule.forRoot({ ...fromRootStore.reducers, companies: combineReducers(fromStore.reducers) }), EffectsModule.forRoot([CompanyEffects, CollectionEffects]), StoreRouterConnectingModule.forRoot() ], providers: [ CompanyService, ...fromCompanyGuards.guards, { provide: RouterStateSerializer, useClass: CustomSerializer } ], declarations: [ CompanySummaryComponent, CompanyHeaderComponent ] }).compileComponents(); // 初始化路由状态 const router = TestBed.inject(Router); await router.navigate(['/companies/test-id']); }));
现在运行你的测试用例,根Store的getRouterState应该能正常返回路由状态,访问user切片的Selector也能正常工作了。
内容的提问来源于stack exchange,提问作者Bhagavan Sankuratri

