Angular 10 测试 ViewContainerRef 的注入器 returns 未定义

Angular 10 testing ViewContainerRef's injector returns undefined

将我们的项目从 Angular 8.2.14 迁移到 Angular 10.2.24 后出现问题。

这是测试代码

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();
    });
});

这是组件的代码

@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;
    }
}

添加了一些日志,看哪部分是undefined,其实viewContainer.injector是未定义的

这段代码过去确实有效,只是在迁移后 运行 失败了。

我试过了

请帮助解决问题。

我仍然找不到在 Angular 10 上不起作用的原因。但是,我的想法是用 TestBed 替换 undefined 喷油器。我发现 TestBed 本身就是 Injector

在测试代码中,我将把这一行从

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

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

并在 PopupModalService 中,将 create 方法更改为

    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;
    }

我将最后一个参数 injector 设为可选,这将保持现有代码正常工作而无需任何更改。

更新:更好的是,我们可以只更新测试代码,就像这样

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

这和上面的一样,好处是我们不碰正常代码,我们只更改测试代码。