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

测试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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:41:21