Problem: I would like to be able to unit test a directive in Angular 2 to make sure that it properly compiles.
In Angular 1, it was possible to use$compile(angular.element(myElement)
service and call $scope.$digest()
after that. I specifically want to be able to do this in unit tests so I could test that when Angular ends up running across <div my-attr-directive/>
in the code that my-attr-directive
compiles.
Constraints:
A mock directive in Angular tests can be created by MockDirective function. The mock directive has the same interface as its original directive, but all its methods are dummies. In order to create a mock directive, pass the original directive into MockDirective function.
We should Unit Test directives by mocking all dependencies with jasmine mocks and spies. We should also Shallow / Deep Test directives using concrete Components (Compiled DOM). A reasonable approach is to create TestComponent or pick up any component which uses the directive we want to test.
If Angular CLI is used to manage the Angular projects, it will automatically support Jasmine and Karma Configurations. All you need in order to test your application is to type the command ng test. As far as possible, run tests on real browsers and devices to ensure that software is verified under real user conditions.
Let's say you have a following directive:
@Directive({ selector: '[my-directive]', }) class MyDirective { public directiveProperty = 'hi!'; }
What you have to do, is to create a component that uses the directive (it can be just for testing purpose):
@Component({ selector: 'my-test-component', template: '' }) class TestComponent {}
Now you need to create a module that has them declared:
describe('App', () => { beforeEach(() => { TestBed.configureTestingModule({ declarations: [ TestComponent, MyDirective ] }); }); // ... });
You can add the template (that contains directive) to the component, but it can be handled dynamically by overwriting the template in test:
it('should be able to test directive', async(() => { TestBed.overrideComponent(TestComponent, { set: { template: '<div my-directive></div>' } }); // ... }));
Now you can try to compile the component, and query it using By.directive
. At the very end, there is a possibility to get a directive instance using the injector
:
TestBed.compileComponents().then(() => { const fixture = TestBed.createComponent(TestComponent); const directiveEl = fixture.debugElement.query(By.directive(MyDirective)); expect(directiveEl).not.toBeNull(); const directiveInstance = directiveEl.injector.get(MyDirective); expect(directiveInstance.directiveProperty).toBe('hi!'); });
To test a directive you need to create a fake component with it:
@Component({ selector: 'test-cmp', directives: [MyAttrDirective], template: '' }) class TestComponent {}
You can add the template in the component itself but it can be handled dynamically by overwriting the template in test:
it('Should setup with conversation', inject([TestComponentBuilder], (testComponentBuilder: TestComponentBuilder) => { return testComponentBuilder .overrideTemplate(TestComponent, `<div my-attr-directive></div>`) .createAsync(TestComponent) .then((fixture: ComponentFixture<TestComponent>) => { fixture.detectChanges(); const directiveEl = fixture.debugElement.query(By.css('[my-attr-directive]')); expect(directiveEl.nativeElement).toBeDefined(); }); }));
Note that you're able to test what directive renders but I couldn't find the way to test a directive in a way components are (there is no TestComponentBuilder for directives).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With