Testing Angular Directives

Using HTMLElement

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.

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 @angular/platform-browser .

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.

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 TestBed and 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.

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 CapitalizeDirective .

Finally, we’re creating a new instance of that component and setting the component variable.

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.

expect(component).toBeDefined();

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.

p.click();

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:

fixture.detectChanges();

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.

expect(component.clickCount).toEqual(1);

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.

expect(p.style.textTransform).toBe('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.

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.

Test Results

WOW!!!!!!!!

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.

Software engineer, writer, traveler, weight lifter

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store