如何为带有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
相关产品推荐
相关产品推荐

