Angular 10 testing ViewContainerRef's injector returns undefined

∥☆過路亽.° 提交于 2021-01-29 08:11:18

问题


The problem happens after migrating our project from Angular 8.2.14 to Angular 10.2.24.

This is the test code

fdescribe('PopupModalService Testing', () => {
    let componentFactoryResolver: ComponentFactoryResolver;
    let viewContainerRef: ViewContainerRef;
    let popupModalService: PopupModalService;

    beforeEach(waitForAsync(() => {
        const viewContainerRefSpy = jasmine.createSpyObj('ViewContainerRef', ['insert']);

        TestBed.configureTestingModule({
            declarations: [
                PopupModalComponent,
                DialogApiComponent,
                BrokerFormPopoverComponent,
                BrokerContextMenuComponent
            ],
            imports: [
                ReactiveFormsModule,
                TranslateModule.forRoot()
            ],
            providers: [
                { provide: ViewContainerRef, useValue: viewContainerRefSpy },
                PopupModalService
            ],
            schemas: [
                NO_ERRORS_SCHEMA
            ]
        });

        TestBed.overrideModule(BrowserDynamicTestingModule, {
            set: {
                entryComponents: [
                    PopupModalComponent,
                    DialogApiComponent,
                    BrokerFormPopoverComponent,
                    BrokerContextMenuComponent
                ]
            }
        });

        componentFactoryResolver = TestBed.inject(ComponentFactoryResolver);
        viewContainerRef = TestBed.inject(ViewContainerRef);
        popupModalService = new PopupModalService(componentFactoryResolver);
    }));

    it('should create PopupModalComponent', () => {
        expect(popupModalService.create(viewContainerRef, ModalType.SIMPLE, 'Test Title', 'Test Content', PopupActionType.SAVE)).toBeDefined();
    });

    it('should create BrokerContextMenuComponent', () => {
        expect(popupModalService.createBrokerContextMenu(viewContainerRef, 999, 999)).toBeDefined();
    });
});

This is the component's code

@Injectable({
    providedIn: 'root'
})
export class PopupModalService {

    factoryResolver: ComponentFactoryResolver;

    constructor(@Inject(ComponentFactoryResolver) factoryResolver) {
        this.factoryResolver = factoryResolver;
    }

    create(
        viewContainer: ViewContainerRef,
        modalType: ModalType,
        title: string,
        content: string,
        popupActionType?: PopupActionType): PopupModalComponent {

        const factory = this.factoryResolver.resolveComponentFactory(PopupModalComponent);
        const popupRef = factory.create(viewContainer.injector);
        const popup = popupRef.instance;
        popup.modalType = modalType;
        popup.title = title;
        popup.content = content;
        popup.setComponentRef(popupRef);
        popup.popupActionType = popupActionType;
        popup.hide();
        viewContainer.insert(popupRef.hostView);
        popup.initFormValue();
        return popup;
    }
}

After adding some logs to see which part is undefined, it's actually the viewContainer.injector is the undefined one.

This code did work in past, it only failed to run after the migration.

I've tried

  • Angular Directive ViewContainerRef Test Mock.
  • https://angular2.programmingpedia.net/en/tutorial/831/dynamically-add-components-using-viewcontainerref-createcomponent

Please help to fix the issue.


回答1:


I still can't find the reason one it doesn't work on Angular 10. However, my idea is to replace the undefined injector by TestBed. I've discovered that TestBed is a Injector itself.

In the test code, I will change this line from

expect(popupModalService.create(viewContainerRef, ModalType.SIMPLE, 'Test Title', 'Test Content', PopupActionType.SAVE)).toBeDefined();

to

expect(popupModalService.create(viewContainerRef, ModalType.SIMPLE, 'Test Title', 'Test Content', PopupActionType.SAVE, TestBed)).toBeDefined();

and in the PopupModalService, change the create method to

    create(
        viewContainer: ViewContainerRef,
        modalType: ModalType,
        title: string,
        content: string,
        popupActionType?: PopupActionType,
        injector?: Injector): PopupModalComponent {

        const factory = this.factoryResolver.resolveComponentFactory(PopupModalComponent);
        const popupRef = factory.create(injector === undefined ? viewContainer.injector : injector);
        const popup = popupRef.instance;
        popup.modalType = modalType;
        popup.title = title;
        popup.content = content;
        popup.setComponentRef(popupRef);
        popup.popupActionType = popupActionType;
        popup.hide();
        viewContainer.insert(popupRef.hostView);
        popup.initFormValue();
        return popup;
    }

I make the last param injector optional, this will keep the existing code work normally without any changes.

UPDATE: Better yet, we can update only the test code, like this

const viewContainerRefSpy = jasmine.createSpyObj('ViewContainerRef', ['insert'], {'injector': TestBed});

This works the same as above, the benefit is we don't touch the normal code, we only change the test code.



来源:https://stackoverflow.com/questions/65355414/angular-10-testing-viewcontainerrefs-injector-returns-undefined

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!