How to Unit Test a Directive In Angular 2?
Solution 1
Testing compiled directive using TestBed
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!');
});
# Old answer:
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).
Solution 2
Took me a while to find a good example, a good person on angular gitter channel pointed me to look at the Angular Material Design 2 repository for examples. You can find a Directive test example here. This is the test file for the tooltip directive of Material Design 2. It looks like you have to test it as part of a component.
Related videos on Youtube
daniel.caspers
SOreadytohelp Check out my personal site for more info!
Updated on July 09, 2022Comments
-
daniel.caspers almost 2 years
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 thatmy-attr-directive
compiles.Constraints:
- Angular 2 using JAVASCRIPT. All sources somewhat related seem to require TS. Perhaps this resource truly does solve the problem and my understanding of TS is just that weak
- Unit Test in Jasmine
- Must be not be so hacky that my unit tests will eventually break. See a related SO post on compiling HTML manually in Angular 2
-
Günter Zöchbauer about 8 yearsYou use
TestComponentBuilder
as shown in the lined SO question/answer. Create a test component where the directive is used in the template and then get a reference to the directive from the created test component instance. -
daniel.caspers about 8 yearsThanks for answering both. Just realized that you answered that question previously! I thought there was a comment on that answer regarding removing support for
TestComponentBuilder
when it got moved to a different package, but I think I'm blurring SO posts in my mind. Thanks. I'll close this one out?
-
daniel.caspers over 7 yearsCool resource! Thanks for sharing. It doesn't meet my requirements of the question of being a JS file.
-
Jusef over 7 yearsIt is difficult to find any resources in pure JS. One idea would be to get the material design repo, compile it and have a look at the .js file that it produces. I will be surprised by how clean is the JavaScript code produced by the Typescript compiler
-
Jusef over 7 yearsIt is difficult to find any resources in pure JS at the moment. One idea would be to get the material design repo, compile it and have a look at the .js file that it produces. You will be surprised by how clean is the JavaScript code produced by the Typescript compiler
-
Rahul Singh about 7 yearsI am getting error : No provider for DirectiveResolver what could be the possible cause?
-
stevek-pro about 7 yearsTo make use of By you will need to
import { By } from '@angular/platform-browser';
-
Rolando about 7 yearswhat about the difective has @input member?, I am interested to add unit test to this control: github.com/czeckd/angular-dual-listbox.. but i don't know how to start.
-
Drenai over 6 years@Wojchieh -
const directiveEl = fixture.debugElement.query(By.directive(MyDirective));
is really helpful. Great answer -
maxime1992 almost 6 yearsAccessing directive instance though injector: Brilliant :) I'll remember that. Thanks
-
Rohìt Jíndal almost 6 yearsGreat explanation. Works like a champ :)
-
JayChase almost 5 yearsThat's a good example to follow. It looks like the repo has been re-organized. The tooltip spec is now here
-
vibhu almost 5 yearsInstead of accessing the Directive instance through injector why not just define a property on the TestComponent as follows:
@ViewChild(MyDirective) directiveInstance: MyDirective
and then use it in your test case like thisexpect(fixture.componentInstance.directiveInstance.directiveProperty).toBe('hi!');