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

如何为带有canActivate守卫的Angular路由编写单元测试

问题根因

你当前的测试失败是因为测试环境中没有对AuthGuard的依赖StorageService做模拟桩,默认调用getLocalStorageItem('auth')返回null,触发了守卫的拦截逻辑,自动重定向到根路由/,因此断言location.path() === '/home'失败。

修复后完整测试代码
import { TestBed, fakeAsync, flush } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { Router, Routes } from '@angular/router';
import { Location } from '@angular/common';
import { AuthGuard } from './auth-guard.guard';
import { StorageService } from './storage.service'; // 替换为你项目中实际的文件路径
import { LoginComponent } from './login.component';
import { HomeComponent } from './home.component';
import { routes } from './app-routing.module';

describe('Testing Router', () => {
  let router: Router;
  let location: Location;
  let storageService: jasmine.SpyObj<StorageService>;

  beforeEach(() => {
    // 创建StorageService的模拟对象,提前声明要模拟的方法
    const storageSpy = jasmine.createSpyObj('StorageService', ['getLocalStorageItem']);

    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule.withRoutes(routes),
        // 也可以用mock组件替代真实组件减少测试依赖
      ],
      providers: [
        AuthGuard,
        { provide: StorageService, useValue: storageSpy }
      ]
    });

    router = TestBed.inject(Router);
    location = TestBed.inject(Location);
    storageService = TestBed.inject(StorageService) as jasmine.SpyObj<StorageService>;
    // 初始化测试路由
    router.initialNavigation();
  });

  // 场景1:已登录状态,访问/home可正常进入
  test(`已登录状态下导航到'home'会进入/home路径`, fakeAsync(() => {
    // 模拟Storage返回已登录的auth信息
    storageService.getLocalStorageItem.and.returnValue(JSON.stringify({ token: 'test_token', userId: 1 }));
    
    router.navigate(['/home']);
    flush();

    expect(location.path()).toEqual('/home');
  }));

  // 场景2:未登录状态,访问/home自动重定向到登录页
  test(`未登录状态下导航到'home'会重定向到根路径`, fakeAsync(() => {
    // 模拟Storage返回null,代表未登录
    storageService.getLocalStorageItem.and.returnValue(null);
    
    router.navigate(['/home']);
    flush();

    expect(location.path()).toEqual('/?returnUrl=%2Fhome');
  }));
});
核心原理说明
  • 路由守卫属于Angular路由导航生命周期的强制校验环节:调用router.navigate时,Angular会先执行目标路由配置的所有canActivate守卫,只有所有守卫都返回true才会进入目标路由,否则会执行守卫返回的重定向逻辑。
  • 单元测试需要显式Mock所有外部依赖:AuthGuard依赖的Router已经由RouterTestingModule提供了测试环境的实现,但是自定义的StorageService需要我们手动创建模拟对象,通过控制模拟对象的返回值,覆盖不同的业务场景测试。
  • 路由导航是异步逻辑,使用fakeAsync+flush可以同步化执行所有异步任务,确保导航逻辑完全执行完成后再执行断言,避免异步时序导致的断言错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 16:06:04