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