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});
这和上面的一样,好处是我们不碰正常代码,我们只更改测试代码。
将我们的项目从 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});
这和上面的一样,好处是我们不碰正常代码,我们只更改测试代码。