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+,
ReflectiveInjectoris deprecated. Switch toInjector.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
mycomptemplate has<ng-container #modal></ng-container>(or another element tagged with#modal)—otherwise,@ViewChildwon’t find theViewContainerRefand your tests will fail.
内容的提问来源于stack exchange,提问作者Mac_W

