Angular函数式路由解析器单元测试示例求助
Angular函数式路由解析器单元测试示例及NG0203错误解决
Angular中基于类的路由解析器已被弃用,官方推荐使用函数式路由解析器。我找不到这类解析器的单元测试示例,且Angular测试环境配置难度较大,比如如何测试下面这个官方示例中的解析器:
export const heroResolver: ResolveFn<Hero> = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => { return inject(HeroService).getHero(route.paramMap.get('id')!); };
我的解析器逻辑和该示例一致,都是调用服务通过ID获取实体,但用Jest调用解析器时出现以下错误:
NG0203: inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with `EnvironmentInjector#runInContext`. Find more at https://angular.io/errors/NG0203
解决方法及测试示例
错误原因是inject()必须在Angular的注入上下文内调用,直接在测试中执行解析器函数会脱离这个上下文。以下是两种可行的测试方案:
方案一:使用TestBed创建注入上下文(推荐)
通过TestBed配置模拟服务,再利用EnvironmentInjector#runInContext在正确的上下文内执行解析器:
Jest测试代码示例:
import { TestBed } from '@angular/core/testing'; import { ActivatedRouteSnapshot, RouterStateSnapshot, ResolveFn } from '@angular/router'; import { heroResolver, Hero } from './hero.resolver'; import { HeroService } from './hero.service'; // 模拟HeroService jest.mock('./hero.service'); describe('heroResolver', () => { let mockHeroService: jest.Mocked<HeroService>; let resolver: ResolveFn<Hero>; beforeEach(() => { // 初始化模拟服务 mockHeroService = new HeroService() as jest.Mocked<HeroService>; mockHeroService.getHero.mockReturnValue({ id: 1, name: '测试英雄' }); // 配置TestBed TestBed.configureTestingModule({ providers: [ { provide: HeroService, useValue: mockHeroService } ] }); resolver = heroResolver; }); it('应通过ID从服务获取并返回英雄', () => { // 模拟路由快照参数 const route = new ActivatedRouteSnapshot(); route.paramMap = { get: jest.fn().mockImplementation(key => key === 'id' ? '1' : null) }; const state = {} as RouterStateSnapshot; // 在注入上下文内执行解析器 const result = TestBed.injector.runInContext(() => resolver(route, state)); // 断言结果 expect(mockHeroService.getHero).toHaveBeenCalledWith('1'); expect(result).toEqual({ id: 1, name: '测试英雄' }); }); });
方案二:使用TestBed与Jasmine(若用Jasmine测试框架)
import { TestBed } from '@angular/core/testing'; import { ActivatedRouteSnapshot, RouterStateSnapshot, ResolveFn } from '@angular/router'; import { heroResolver, Hero } from './hero.resolver'; import { HeroService } from './hero.service'; describe('heroResolver', () => { let mockHeroService: jasmine.SpyObj<HeroService>; let resolver: ResolveFn<Hero>; beforeEach(() => { // 创建Spy对象模拟服务 mockHeroService = jasmine.createSpyObj('HeroService', ['getHero']); TestBed.configureTestingModule({ providers: [ { provide: HeroService, useValue: mockHeroService } ] }); resolver = heroResolver; }); it('应返回HeroService获取的英雄', () => { const mockHero: Hero = { id: 1, name: '测试英雄' }; mockHeroService.getHero.and.returnValue(mockHero); // 模拟路由参数 const route = new ActivatedRouteSnapshot(); route.paramMap = { get: (key: string) => key === 'id' ? '1' : null } as any; const state = {} as RouterStateSnapshot; // 在注入上下文执行解析器 const result = TestBed.injector.runInContext(() => resolver(route, state)); expect(result).toEqual(mockHero); expect(mockHeroService.getHero).toHaveBeenCalledWith('1'); }); });
核心原理
TestBed.injector.runInContext()会将传入的函数包裹在Angular的注入环境中执行,这样inject()就能正确获取到依赖的服务,避免NG0203错误。
内容的提问来源于stack exchange,提问作者ajp11
相关产品推荐
相关产品推荐

