Angular函数式路由守卫测试报错NG0203,求助解决inject()调用上下文问题
Angular函数式路由守卫测试报错NG0203,求助解决inject()调用上下文问题
兄弟我刚踩过这个坑!这个NG0203错误本质是:函数式守卫里的inject()方法必须在Angular的注入上下文环境中才能调用,你直接在测试的it块里调用authGuard的话,没有这个上下文加持,inject()根本找不到要注入的服务,自然就报错了😅
给你两个关键修复方向,直接改测试代码就能解决:
核心修复思路:用TestBed.runInInjectionContext()包裹守卫调用
Angular提供了runInInjectionContext()方法,专门用来帮我们在测试环境中创建合法的注入上下文,让inject()能正常工作。你只需要把调用authGuard的代码放到这个方法的回调里就行。
修改后的完整测试代码
import { TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { KeycloakService } from 'keycloak-angular'; import { authGuard } from './auth.guard'; describe('authGuard', () => { let router: Router; // Step 1: Mock KeycloakService(调整位置让代码结构更清晰) class MockKeycloakService { isLoggedIn(): Promise<boolean> { return Promise.resolve(true); } login(options: any): Promise<void> { return Promise.resolve(); } } beforeEach(() => { TestBed.configureTestingModule({ imports: [RouterTestingModule], providers: [{ provide: KeycloakService, useClass: MockKeycloakService }], }); router = TestBed.inject(Router); }); it('should allow access when the user is logged in', async () => { // Arrange const route: ActivatedRouteSnapshot = {} as any; const state: RouterStateSnapshot = {} as any; // Act:用runInInjectionContext包裹守卫调用 const result = await TestBed.runInInjectionContext(() => { return authGuard(route, state); }); // Assert expect(result).toBe(true); }); it('should redirect to login when the user is not logged in', async () => { // Arrange const route: ActivatedRouteSnapshot = {} as any; const state: RouterStateSnapshot = { url: '/consent-initiation', } as any; // 在注入上下文里获取mock服务并设置spy let keycloakService: MockKeycloakService; await TestBed.runInInjectionContext(() => { keycloakService = TestBed.inject(KeycloakService) as any; spyOn(keycloakService, 'isLoggedIn').and.returnValue(Promise.resolve(false)); spyOn(keycloakService, 'login').and.returnValue(Promise.resolve()); }); // Act:同样用runInInjectionContext包裹 const result = await TestBed.runInInjectionContext(() => { return authGuard(route, state); }); // Assert expect(result).toBe(false); // 顺便验证login方法的调用参数是否符合预期 expect(keycloakService.login).toHaveBeenCalledWith({ redirectUri: window.location.origin + state.url }); }); });
额外说明
- 我把
MockKeycloakService提到了describe顶部,这样测试代码结构更清晰,也符合Angular测试的常规写法。 - 第二个测试用例里,我们需要在注入上下文里获取mock服务并设置spy,这样才能确保spy作用的是守卫实际会用到的那个服务实例。
runInInjectionContext是Angular 14及以上版本提供的API,刚好适配你用的函数式守卫(函数式守卫也是14+引入的),完全匹配你的场景。
备注:内容来源于stack exchange,提问作者DevDuke
相关产品推荐
相关产品推荐

