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

Angular指令通过inject获取宿主组件后Jest测试报NG0203错误求助

解决Angular指令中inject()在Jest测试里的NG0203错误

问题场景

我有一个指令,通过inject关键字获取继承自抽象组件MyBaseComponent的实例,指令代码如下:

export class MyDirective implements AfterViewInit {
  private component: MyBaseComponent = inject(MyBaseComponent);
  ...
}

使用时直接在子组件模板上应用指令:

<my-text-component my-directive>
</my-text-component>

正常运行时component能正确指向my-text-component实例,但Jest单元测试时触发错误,测试代码如下:

describe("MyDirectiveTest", () => {
  let directive: MyDirective;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [MyDirective, MyBaseComponent, MyTextComponent],
      providers: [{ provide: MyBaseComponent, useClass: MyTextComponent }],
    });
    directive = new MyDirective();
  });

  it("should initialize correctly", () => {
    expect(directive).toBeTruthy();
  });
});

错误信息:

NG0203: inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with runInInjectionContext.

已尝试给指令添加@Injectable()装饰器,无效果。


原因分析

直接用new MyDirective()手动创建实例时,脱离了Angular的注入上下文环境,inject()无法找到对应的依赖提供者,因此触发报错。指令的依赖必须通过TestBed的注入机制获取,不能手动实例化。


解决方案

方案1:模拟真实场景,用宿主组件承载指令

这种方式最贴近实际运行逻辑,因为指令本身就是依附在组件上生效的:

describe("MyDirectiveTest", () => {
  let fixture: ComponentFixture<TestHostComponent>;
  let directive: MyDirective;
  let textComponent: MyTextComponent;

  // 创建测试宿主组件,用来模拟指令的实际使用场景
  @Component({
    template: `<my-text-component my-directive></my-text-component>`
  })
  class TestHostComponent {}

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [
        MyDirective,
        MyBaseComponent,
        MyTextComponent,
        TestHostComponent
      ]
    });

    fixture = TestBed.createComponent(TestHostComponent);
    fixture.detectChanges(); // 触发组件初始化

    // 从DOM元素中查询获取指令和组件实例
    textComponent = fixture.debugElement.query(By.directive(MyTextComponent)).componentInstance;
    directive = fixture.debugElement.query(By.directive(MyDirective)).injector.get(MyDirective);
  });

  it("should initialize correctly", () => {
    expect(directive).toBeTruthy();
    // 验证指令获取的component是正确的MyTextComponent实例
    expect(directive['component']).toBe(textComponent);
  });
});

方案2:用runInInjectionContext包裹实例化

如果只想单独测试指令逻辑,不想依赖组件模板,可以用此方法确保inject()在正确上下文执行:

describe("MyDirectiveTest", () => {
  let directive: MyDirective;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [MyDirective, MyBaseComponent, MyTextComponent],
      providers: [{ provide: MyBaseComponent, useClass: MyTextComponent }],
    });

    const injector = TestBed.inject(Injector);
    directive = runInInjectionContext(injector, () => new MyDirective());
  });

  it("should initialize correctly", () => {
    expect(directive).toBeTruthy();
  });
});

关键要点

  • 指令中inject(MyBaseComponent)的本质是获取指令所在元素的组件实例,测试时必须模拟这种层级依赖关系。
  • 永远不要手动newAngular的指令/组件实例,必须通过TestBed的注入机制或组件Fixture来获取实例,保证依赖注入上下文的正确性。

内容的提问来源于stack exchange,提问作者A.B.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 00:42:55