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

如何为依赖createUrlTreeFromSnapshot的Angular17 CanActivateFn守卫编写Jest测试

Angular 17.1 CanActivateFn 守卫单元测试解决方案

问题根源

createUrlTreeFromSnapshot需要完整的路由快照上下文(包含parent、router等核心属性),直接传入空的ActivatedRouteSnapshot会因缺少必要字段抛出Cannot read properties of undefined (reading 'children')错误。

正确测试实现步骤

1. 初始化测试环境与依赖模拟

使用TestBed配置测试模块,模拟CoreFacade和路由相关依赖,构建具备完整上下文的ActivatedRouteSnapshot:

import { TestBed } from '@angular/core/testing';
import { CanActivateFn, ActivatedRouteSnapshot, Router, UrlTree } from '@angular/router';
import { showPensionInfoGuard } from './allow-navi.guard';
import { CoreFacade } from '@mylib/core';
import { of } from 'rxjs';

describe('showPensionInfoGuard', () => {
  let guard: CanActivateFn;
  let mockCoreFacade: jasmine.SpyObj<CoreFacade>;
  let mockRouter: jasmine.SpyObj<Router>;
  let mockRoute: ActivatedRouteSnapshot;

  beforeEach(() => {
    // 模拟CoreFacade,动态控制allowNavigation$的返回值
    mockCoreFacade = jasmine.createSpyObj('CoreFacade', [], { allowNavigation$: of(null) });
    // 模拟Router,拦截createUrlTree调用
    mockRouter = jasmine.createSpyObj('Router', ['createUrlTree']);
    
    // 构建带parent的路由快照,支持相对路径解析
    const parentRoute = new ActivatedRouteSnapshot();
    parentRoute.url = [{ path: 'parent' }];
    
    mockRoute = new ActivatedRouteSnapshot();
    mockRoute.router = mockRouter;
    mockRoute.parent = parentRoute;

    TestBed.configureTestingModule({
      providers: [
        { provide: CoreFacade, useValue: mockCoreFacade },
        { provide: Router, useValue: mockRouter }
      ]
    });

    guard = TestBed.inject(showPensionInfoGuard);
  });
});

2. 测试允许导航场景(allowNavigation$ 返回 false)

it('允许导航当 allowNavigation$ 返回 false', (done) => {
  mockCoreFacade.allowNavigation$ = of(false);

  guard(mockRoute).subscribe({
    next: (result) => {
      expect(result).toBe(true);
      done();
    },
    error: (err) => done.fail(err)
  });
});

3. 测试跳转路由场景(allowNavigation$ 返回 true)

验证createUrlTreeFromSnapshot是否正确生成指向../main的UrlTree,通过拦截Router的createUrlTree方法断言调用参数:

it('返回指向 ../main 的 UrlTree 当 allowNavigation$ 返回 true', (done) => {
  mockCoreFacade.allowNavigation$ = of(true);
  const mockUrlTree = {} as UrlTree;
  mockRouter.createUrlTree.and.returnValue(mockUrlTree);

  guard(mockRoute).subscribe({
    next: (result) => {
      expect(mockRouter.createUrlTree).toHaveBeenCalledWith(['../main'], { relativeTo: mockRoute });
      expect(result).toBe(mockUrlTree);
      done();
    },
    error: (err) => done.fail(err)
  });
});

4. 测试null值过滤逻辑

确保守卫会忽略allowNavigation$发出的null值,仅处理非null的布尔状态:

it('忽略 allowNavigation$ 发出的 null 值', (done) => {
  let isEmitted = false;
  mockCoreFacade.allowNavigation$ = of(null, false);

  guard(mockRoute).subscribe({
    next: (result) => {
      isEmitted = true;
      expect(result).toBe(true);
      done();
    }
  });

  setTimeout(() => {
    if (!isEmitted) done.fail('守卫未在收到非null值后触发输出');
  }, 100);
});

核心注意事项

  • 必须给ActivatedRouteSnapshot设置router和parent属性,否则相对路径解析会失败
  • 动态切换mockCoreFacade.allowNavigation$的返回值,覆盖所有业务场景
  • 异步测试必须使用done回调,确保Observable完成后再断言

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 02:15:10