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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 21:33:21