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

Angular 4:使用ReflectiveInjector的组件单元测试编写遇阻求助

Hey, let's work through this unit testing challenge for your dynamic Angular component setup! I’ve dealt with similar ViewContainerRef and injector scenarios before, so here’s a step-by-step approach to test your mycomp component effectively.

First, Let's Confirm the Full Component & Test Setup

First, I’ll assume your component’s template includes the #modal container (since you’re using @ViewChild to grab it). Here’s the full component code plus a test dynamic component we’ll use for testing:

import { Component, ViewContainerRef, ReflectiveInjector, Input, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';

// Your component under test
export class mycomp{
 currentComponent: any;
 @ViewChild('modal', { read: ViewContainerRef }) modal: ViewContainerRef;
 @Input() set componentData(data: any) {
  if (!data) {
   return;
  }
  const inputProviders = Object.keys(data.inputs).map((inputName) => {
   return {provide: inputName, useValue: data.inputs[inputName]};
  });
  const resolvedInputs = ReflectiveInjector.resolve(inputProviders);
  const injector = ReflectiveInjector.fromResolvedProviders(resolvedInputs, this.modal.injector);
  this.currentComponent = this.modal.createComponent(data.component, 0, injector);
 }
}

// Test dynamic component to inject during tests
@Component({
  template: '<p>{{testInput}}</p>'
})
class TestDynamicComponent {
  @Input() testInput: string;
}

1. Configure the TestBed

We need to declare both your component and our test dynamic component in the TestBed setup:

describe('mycomp', () => {
  let component: mycomp;
  let fixture: ComponentFixture<mycomp>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [mycomp, TestDynamicComponent]
    }).compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(mycomp);
    component = fixture.componentInstance;
    fixture.detectChanges(); // Trigger initial data binding
  });

2. Test the componentData Setter Logic

Let’s cover the key scenarios for the setter:

Case 1: Do Nothing When componentData is Null/Undefined

it('should ignore null/undefined componentData', () => {
    component.componentData = null;
    expect(component.currentComponent).toBeUndefined();

    component.componentData = undefined;
    expect(component.currentComponent).toBeUndefined();
  });

Case 2: Create Dynamic Component with Provided Inputs

This test verifies that the dynamic component is created, inputs are passed correctly, and the template renders as expected:

it('should create dynamic component with injected inputs', () => {
    const testInputValue = 'Hello Dynamic World!';
    
    component.componentData = {
      component: TestDynamicComponent,
      inputs: { testInput: testInputValue }
    };

    fixture.detectChanges(); // Trigger the component creation

    // Check if the component instance exists
    expect(component.currentComponent).toBeTruthy();
    // Verify the input was passed correctly
    expect(component.currentComponent.instance.testInput).toBe(testInputValue);
    // Check the rendered output matches the input
    const dynamicElement = fixture.nativeElement.querySelector('p');
    expect(dynamicElement.textContent.trim()).toBe(testInputValue);
  });

Case 3: Handle Empty Inputs Object

Make sure the component still creates even if no inputs are provided:

it('should create dynamic component without inputs', () => {
    component.componentData = {
      component: TestDynamicComponent,
      inputs: {}
    };

    fixture.detectChanges();

    expect(component.currentComponent).toBeTruthy();
    expect(component.currentComponent.instance.testInput).toBeUndefined();
  });
});

Bonus: Mocking ViewContainerRef (For Isolated Testing)

If you want to avoid relying on the real DOM container, you can mock ViewContainerRef to test the injector logic without rendering components:

describe('mycomp (isolated)', () => {
  let component: mycomp;
  let mockViewContainer: jasmine.SpyObj<ViewContainerRef>;

  beforeEach(() => {
    // Create a mock with the createComponent method
    mockViewContainer = jasmine.createSpyObj('ViewContainerRef', ['createComponent']);
    component = new mycomp();
    // Assign the mock to the component's modal property
    component.modal = mockViewContainer;
  });

  it('should call createComponent with the right parameters', () => {
    const testData = {
      component: TestDynamicComponent,
      inputs: { testInput: 'Mock Test' }
    };

    component.componentData = testData;

    // Verify createComponent was called with the correct component and injector
    expect(mockViewContainer.createComponent).toHaveBeenCalledWith(
      testData.component,
      0,
      jasmine.any(ReflectiveInjector)
    );
  });
});

Quick Notes

  • Angular Version Heads-Up: If you’re using Angular 9+, ReflectiveInjector is deprecated. Switch to Injector.create() instead—your test logic will stay almost identical, just replace the injector creation code:
    const injector = Injector.create({
      providers: inputProviders,
      parent: this.modal.injector
    });
    
  • Template Check: Ensure your mycomp template has <ng-container #modal></ng-container> (or another element tagged with #modal)—otherwise, @ViewChild won’t find the ViewContainerRef and your tests will fail.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:25:34