In this example I wanted to show you how you can dive down into the DOM to find elements in your Angular templates. This can be used to make sure your Angular directives are working properly when writing tests. In order to do this we’ll be tapping into the power of the HTMLElement API. This will make it possible to manipulate and read properties of elements in our component’s HTML template.
* NOTE: This article is not meant to be a full scale tutorial on how to do testing in Angular . For more information on that try this.
What Angular’s testing module gives you
By default, Angular comes with some great utility classes to use while writing tests. These can mostly be imported from
@angular/core/testing however, there are other tools you can use like the
By class which can also be used to query the DOM. This can be found in
The main class we’ll be discussing in this example is the
ComponentFixture class. This class acts as a wrapper around our actual component that comes with some added behavior. A Fixture has a property that exposes out component’s HTML as a
nativeElement , which is exactly what we need to start interacting with the DOM.
In order to tap into this power we first need to create our directive to test.
Here we see a simple directive that only does one thing. It toggles the text’s case. It does this by importing the
ElementRef class and using that API to grab the
nativeElement of the host component. This is a crucial step that shows us where we need to look later when testing this directive. Using the
@HostListener() decorator gives us the ability to listen to events, and then react.
Accessing the DOM
Now we have our directive setup and we’re ready to test it! Let’s take a look at the completed test file and then break it down piece by piece.
I’ve added some comments in the code to help show what I’m trying to do.
Let’s start at the top and work our way down.
You can see we’re importing everything we’ll need to get this done. First, because this is a spec file we’ll need to import the testing classes like
ComponentFixture. We are also importing our directive because, well, it’s kind of hard to test it without that. Then lastly, we’re importing some basic Angular building classes that will help us create our test component.
Creating the test component
Sometimes we want to test a directive but we don’t need to test it on a real component. In this case, we can simply create a test component in our spec file as we have here.
This allows us to avoid importing unnecessary dependencies or worrying about creating stubs we don’t need. This component can stay very basic and still allow us to test the directive. However, in order to help us further illustrate this lesson, we add a
clickCount property in this component to help us track how many clicks have happened on the component. This is for demonstration purposes only in this case.
Now we have our
TestComponent created we can go ahead and set up our test. First, we create our
describe block to house our test logic. Then we create two very important references, the component and the fixture of that component. Remember a fixture is just a wrapper of a component.
Then , inside the
beforeEach(), we initialize the test module. The only thing to note here is that we’re only declaring the
TestComponent and the
Finally, we’re creating a new instance of that component and setting the
What to expect()… when expecting
With everything setup, we can finally start testing! :)
Before we get too excited, let’s create a sanity test to make sure our component is actually being created.
If that passes we know we’re ready for the next test.
Pro Tip: Have
ng test running while you work to make sure you’re not breaking anything.
Now it’s time for the real show to begin. We need to create a test to make sure this component does what it’s meant to. Specifically, when it’s host element is clicked, it toggles the text’s case.
In order to do that we need to do the following:
1: Get the host element
2: Get the child element with the directive attribute
3: Trigger a click event
4: Make sure the properties on that element have changed
We can get the host element, also called the
debugElement, by digging into the fixture.
const debugEl: HTMLElement = fixture.debugElement.nativeElement;
Notice the very end of this line. We’re not just stopping at the debug element, we’re going a step further and getting the
nativeElement and this is the secret to accessing the DOM.
By accessing the native element we are able to “type” the
debugEl as an HTMLElement. Which opens up the whole API for that object.
However, there’s a problem. We don’t have the correct element! We actually want the child
<p> element that actually has the directive on it. To do that we leverage the HTMLElement method
querySelector() which takes a CSS selector parameter and returns the first matching element. In our case, that’s exactly what we want.
Time to start clicking!
HTMLElements have a method called
click() which allow you to programmatically trigger a click event.
This will make our component react exactly the same way as if a user had clicked on it. The only catch is that we need to trigger the Angular change detection cycle manually in order to register that change. For this we do:
Lastly we need to make sure things are updating accordingly. We need to verify two main things, our click counter incremented, and the text is now uppercase.
Checking the component property is a breeze using the component reference we created earlier.
Jasmine gives us some great helper methods like
toEqual() to say what we want in almost plain English.
Then last but not least, we check the style on our paragraph element to make sure the text inside it is uppercase.
If you’re not familiar with the HTMLElement, each element has a set of properties that you can get/set. In this case we want to check the list of styles for this element, then make sure that the value for
text-transform is uppercase.
Our last test is basically the same thing with one small difference. We want to make sure that it is actually toggling the text’s case on click.
All tests passing!
Now that we have everything set up we can run our app with
ng serve then we can run our tests with
ng test and see that all our specs are passing! Nice.
We just did some really cool stuff!
Hopefully, you learned about how to use HTMLElement to dig down into your components to test any property changes you make to the DOM. Once you have the element, you can do anything you want. Let me know if you have a better way to do it, or if I’ve made any mistakes.
Full code can be found here.