Issue
Given a directive used for Dynamic Component Loading with ViewContainerRef injected:
import { Directive, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[fooHost]'
})
export class FooDirective {
constructor(public viewContainerRef: ViewContainerRef) {}
}
How would you inject an instance or mock of ViewContainerRef
in a unit test:
import { FooDirective } from './foo.directive';
describe('FooDirective', () => {
it('should create an instance', () => {
const directive = new FooDirective();
expect(directive).toBeTruthy();
});
});
This most basic test fails due to the following error:
An argument for 'viewContainerRef' was not provided.
The testing guide does not cover this nor does there appear to be any testing module specifically for creating an instance of ViewContainerRef
.
Is this is simple as creating a stub @Component
with TestBed.createComponent
and passing either the fixture or component instance as ViewContainerRef
?
import { FooDirective } from './foo.directive';
import { ViewContainerRef, Component } from '@angular/core';
import { TestBed, async } from '@angular/core/testing';
@Component({ selector: 'app-stub', template: '' })
class StubComponent {}
describe('LightboxDirective', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({ declarations: [StubComponent] }).compileComponents();
}));
it('should create an instance', () => {
const fixture = TestBed.createComponent(StubComponent);
const component = fixture.debugElement.componentInstance;
const directive = new FooDirective(component);
expect(directive).toBeTruthy();
});
});
If that is the case, what should be passed as ViewContainerRef
, the fixture.debugElement.componentInstance
or fixture.debugElement.nativeElement
or something else?
Thanks!
Solution
ViewContainerRef
is an abstract class that is imported from @angular/core
. Because it is an abstract class, it cannot be directly instantiated. However, in your test class, you can simply create a new class which extends
the ViewContainerRef, and implements all of the required methods. Then, you can simply instantiate a new instance of the TestViewContainerRef and pass it into your FooDirective constructor in your test/spec. As such:
// create the test class
class TestViewContainerRef extends ViewContainerRef {
get element(): import("@angular/core").ElementRef<any> {
throw new Error("Method not implemented.");
}
get injector(): import("@angular/core").Injector {
throw new Error("Method not implemented.");
}
get parentInjector(): import("@angular/core").Injector {
throw new Error("Method not implemented.");
}
clear(): void {
throw new Error("Method not implemented.");
}
get(index: number): import("@angular/core").ViewRef {
throw new Error("Method not implemented.");
}
get length(): number {
throw new Error("Method not implemented.");
}
createEmbeddedView<C>(templateRef: import("@angular/core").TemplateRef<C>, context?: C, index?: number): import("@angular/core").EmbeddedViewRef<C> {
throw new Error("Method not implemented.");
}
createComponent<C>(componentFactory: import("@angular/core").ComponentFactory<C>, index?: number, injector?: import("@angular/core").Injector, projectableNodes?: any[][], ngModule?: import("@angular/core").NgModuleRef<any>): import("@angular/core").ComponentRef<C> {
throw new Error("Method not implemented.");
}
insert(viewRef: import("@angular/core").ViewRef, index?: number): import("@angular/core").ViewRef {
throw new Error("Method not implemented.");
}
move(viewRef: import("@angular/core").ViewRef, currentIndex: number): import("@angular/core").ViewRef {
throw new Error("Method not implemented.");
}
indexOf(viewRef: import("@angular/core").ViewRef): number {
throw new Error("Method not implemented.");
}
remove(index?: number): void {
throw new Error("Method not implemented.");
}
detach(index?: number): import("@angular/core").ViewRef {
throw new Error("Method not implemented.");
}
}
Hint: I am using VS Code on a Mac. When I create the class stub class TestViewContainerRef extends ViewContainerRef { }
, Code gives me a very helpful code hint to implement all abstract methods. I used that to automatically generate the code above. Other IDEs may offer similar functionality to help the process go smoother. You may be able to copy/paste the code here to use in your test/spec class. However, Angular may choose to change the interface for the ViewContainerRef abstract class at any time, so if you do copy this code above, be aware that you do so at your own peril.
Here's an example of how I used it to get my Angular tests to pass:
import { ModalHostDirective } from './modal-host.directive';
import { ViewContainerRef } from '@angular/core';
class TestViewContainerRef extends ViewContainerRef {
get element(): import("@angular/core").ElementRef<any> {
throw new Error("Method not implemented.");
}
get injector(): import("@angular/core").Injector {
throw new Error("Method not implemented.");
}
get parentInjector(): import("@angular/core").Injector {
throw new Error("Method not implemented.");
}
clear(): void {
throw new Error("Method not implemented.");
}
get(index: number): import("@angular/core").ViewRef {
throw new Error("Method not implemented.");
}
get length(): number {
throw new Error("Method not implemented.");
}
createEmbeddedView<C>(templateRef: import("@angular/core").TemplateRef<C>, context?: C, index?: number): import("@angular/core").EmbeddedViewRef<C> {
throw new Error("Method not implemented.");
}
createComponent<C>(componentFactory: import("@angular/core").ComponentFactory<C>, index?: number, injector?: import("@angular/core").Injector, projectableNodes?: any[][], ngModule?: import("@angular/core").NgModuleRef<any>): import("@angular/core").ComponentRef<C> {
throw new Error("Method not implemented.");
}
insert(viewRef: import("@angular/core").ViewRef, index?: number): import("@angular/core").ViewRef {
throw new Error("Method not implemented.");
}
move(viewRef: import("@angular/core").ViewRef, currentIndex: number): import("@angular/core").ViewRef {
throw new Error("Method not implemented.");
}
indexOf(viewRef: import("@angular/core").ViewRef): number {
throw new Error("Method not implemented.");
}
remove(index?: number): void {
throw new Error("Method not implemented.");
}
detach(index?: number): import("@angular/core").ViewRef {
throw new Error("Method not implemented.");
}
}
describe('ModalHostDirective', () => {
it('should create an instance', () => {
const directive = new ModalHostDirective(new TestViewContainerRef());
expect(directive).toBeTruthy();
});
});
Disclaimer: As far as actually writing tests against this TestViewContainerRef, well...I leave that to all of you. But this at least satisfies ng test
.
Cheers!
Answered By - Danny Bullis
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.