问题
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