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

使用NgMocks测试canActivate守卫时遇依赖缺失错误

函数式CanActivate守卫的NgMocks测试配置问题

我正在为一个简单的canActivate函数式守卫编写测试,该守卫在应用中可正常工作,但使用NgMocks编写测试时出现错误:

Error: MockBuilder has found a missing dependency: hasGraphResultsCanActiveGuard. It means no module provides it. Please, use the "export" flag if you want to add it explicitly.

尝试添加export flag后,又出现无法解析参数的问题。相关代码如下:

// guard
export const hasGraphResultsCanActiveGuard: CanActivateFn = (
  route: ActivatedRouteSnapshot,
  routerState: RouterStateSnapshot
) => {
  return inject(Store)
    .select(selectGraphResults)
    .pipe(
      map((results) => {
        return results.length > 0;
      }),
      tap((hasResults) => {
        if (!hasResults) {
          inject(Router).navigate(['/app/map']);
        }
      })
    );
};
// routes in the module where its used
const routes: Routes = [
  {
    path: 'analysis',
    component: AnalysisComponent,
    canActivate: [hasGraphResultsCanActiveGuard],
  },
  {
    path: '**',
    pathMatch: 'full',
    redirectTo: 'analysis',
  },
];
// Test
describe('HasGraphResultsGuard', () => {
  beforeEach(() => {
    return (
      MockBuilder(
        [
          RouterModule,
          RouterTestingModule.withRoutes([]),
          NG_MOCKS_ROOT_PROVIDERS,
        ],
        DisruptionPageModule
      )
        .exclude(NG_MOCKS_GUARDS)
        .keep(hasGraphResultsCanActiveGuard)
        .provide(
          provideMockStore({
            initialState: {
              graph: cloneDeep(initialGraphState),
            },
          })
        )
    );
  });

  it('Redirects to /app/map', fakeAsync(() => {
    const fixture = MockRender(RouterOutlet, {});
    const router = ngMocks.get(Router);
    const location = ngMocks.get(Location);

    // Initialize navigation
    if (fixture.ngZone) {
      fixture.ngZone.run(() => router.initialNavigation());
      tick();
    }

    // The initial state has no graph results so it should redirect
    expect(location.path()).toEqual('/app/map');
  }));
});

请问我是否遗漏了配置项,或是文档存在缺失?


解决方案

问题出在函数式守卫(CanActivateFn)的测试配置逻辑上,和类守卫的处理方式不同,调整后的测试配置如下:

describe('HasGraphResultsGuard', () => {
  beforeEach(() => {
    return MockBuilder(DisruptionPageModule)
      // 排除默认的守卫模拟,避免冲突
      .exclude(NG_MOCKS_GUARDS)
      // 配置测试路由,确保守卫能被触发,用Mock组件替代真实组件
      .provide(RouterTestingModule.withRoutes([
        {
          path: 'analysis',
          component: ngMocks.mock(Component),
          canActivate: [hasGraphResultsCanActiveGuard],
        },
        {
          path: 'app/map',
          component: ngMocks.mock(Component),
        },
      ]))
      // 提供MockStore并初始化状态
      .provide(provideMockStore({
        initialState: {
          graph: cloneDeep(initialGraphState),
        },
      }))
      // 确保Router相关根依赖被正确提供
      .provide(NG_MOCKS_ROOT_PROVIDERS);
  });

  it('Redirects to /app/map when no graph results exist', fakeAsync(() => {
    const router = ngMocks.get(Router);
    const location = ngMocks.get(Location);

    // 主动导航到需要守卫校验的路由
    router.navigate(['/analysis']);
    tick();

    // 验证重定向结果
    expect(location.path()).toEqual('/app/map');
  }));
});

关键调整说明:

  • 函数式守卫的注册:函数式守卫不是类,不需要用.keep(),直接在测试路由的canActivate数组中引用即可,更贴合实际业务场景。
  • 简化Router配置:不要同时引入RouterModule和RouterTestingModule,直接用RouterTestingModule.withRoutes()配置测试所需的完整路由链路,包括重定向目标的Mock组件。
  • 触发守卫的正确方式:通过router.navigate(['/analysis'])主动触发路由导航,确保守卫逻辑被执行,而非依赖initialNavigation()。
  • 依赖注入校验:确保Store和Router的Mock实例被正确提供,provideMockStore负责状态管理Mock,RouterTestingModule负责路由相关Mock。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 10:17:54