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

Angular14+用inject函数重构组件后,如何在Jest测试中注入依赖?

解决Angular 14+ inject函数与Jest直接实例化组件的兼容问题

核心问题原因

inject()函数必须在Angular的注入上下文(比如组件构造函数执行时、工厂函数、runInInjectionContext包裹的代码块)中调用。直接通过new MyComponent()实例化组件时,没有触发Angular的注入上下文,因此inject()会抛出错误。

解决方案

方案1:组件层兼容(最小改动测试代码)

给组件构造函数添加可选参数,内部用inject()兜底赋值。这样测试时可以手动传入mock依赖,正常运行时Angular会自动注入:

@Component({
  selector: "app-cmpt",
  templateUrl: "./cmpt.component.html",
  styleUrls: ["./cmpt.component.scss"],
})
export class MyComponent implements OnInit {
  private fb: NonNullableFormBuilder;
  private service: MyService;
  private router: Router;

  constructor(
    fb?: NonNullableFormBuilder,
    service?: MyService,
    router?: Router
  ) {
    // 优先使用构造函数传入的参数,否则用inject自动注入
    this.fb = fb ?? inject(NonNullableFormBuilder);
    this.service = service ?? inject(MyService);
    this.router = router ?? inject(Router);
  }

  // 其余组件逻辑...
}

测试代码可以完全保留原来的写法:

describe('MyComponent', () => {
  let component: MyComponent;
  let nnfb = new FormBuilder().nonNullable;
  let serviceSpy: any;
  let routerMock: Partial<Router>;

  beforeEach(() => {
    serviceSpy = {
      get: jest.fn(),
      update: jest.fn(),
      delete: jest.fn()
    };
    
    routerMock = { navigate: jest.fn() };
    
    // 依然可以直接new组件并传入mock依赖
    component = new MyComponent(nnfb, serviceSpy, routerMock as Router);
  });

  // 测试用例...
});

方案2:测试层处理(不改动组件代码)

使用Angular的createInjector和runInInjectionContext手动创建注入上下文,在上下文内实例化组件。这种方式不需要修改组件,且createInjector比TestBed轻量很多,不会大幅降低测试速度:

import { runInInjectionContext, createInjector } from '@angular/core';
import { NonNullableFormBuilder, FormBuilder } from '@angular/forms';
import { Router } from '@angular/router';
import { MyComponent } from './cmpt.component';
import { MyService } from '../services/my.service';

describe('MyComponent', () => {
  let component: MyComponent;
  let serviceSpy: any;
  let routerMock: Partial<Router>;

  beforeEach(() => {
    serviceSpy = {
      get: jest.fn(),
      update: jest.fn(),
      delete: jest.fn()
    };

    routerMock = {
      navigate: jest.fn()
    };

    // 创建轻量注入器,注册mock依赖
    const injector = createInjector({
      providers: [
        { provide: NonNullableFormBuilder, useValue: new FormBuilder().nonNullable },
        { provide: MyService, useValue: serviceSpy },
        { provide: Router, useValue: routerMock }
      ]
    });

    // 在注入上下文中实例化组件
    runInInjectionContext(injector, () => {
      component = new MyComponent();
    });
  });

  // 测试用例示例
  it('should call service.get on init', () => {
    component.ngOnInit();
    expect(serviceSpy.get).toHaveBeenCalled();
  });
});

方案选择建议

  • 如果希望测试代码完全不变,优先选方案1,组件改动极小且不影响生产环境的注入逻辑。
  • 如果不想修改组件代码,优先选方案2,用Angular原生API创建轻量注入上下文,避免TestBed的繁琐配置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 00:45:47