Angular Unit Testing Series — Episode #3: Component testing w/ Jasmine
Testing component & its HTML template
In the previous episode of the series, we tested our application’s service using Jasmine and ts-mocking-bird. This episode is the third and final episode of the Angular unit testing series.
In this article, we're going to go learn the nitty-gritty of testing the component and template of our Demystify Angular application. We shall leverage the Jasmine test framework since the ts-mocking-bird doesn’t support template testing.
What we expect:
A test-pass result covering the main App component and User list component and their templates, which is reported in the browser.
Project codebase status: GitHub link
By the end of this article, you should be able to;
- write a test for Angular component
- write a test for Angular HTML template
Let’s dive right in!…
The User list component
As per our Software architecture, the User list component sits on top of the User service and fetches the users from the service into a user array. It also listens to any error that may occur and handles it by displaying a friendly error message.
The user-list.component.ts
file contains the following code structure:
Our goal is to test for the component considering its core possible behaviors including when the users are successfully fetched and when the request fails.
The User list component spec file
Within the user-list/
folder is the user-list.component.spec.ts
file which contains the default test suite for the main user-list.component.ts
implementation file.
Now, let’s run the default test without refactoring anything.
Running our user list component test
Inside the root of the project directory, demystify, run the command below.
$ ng test
Let’s run only the userListComponent test suit by focusing on it with an ‘f’ like this
fdescribe(‘UserListComponent’...
You will receive a failed test result from the console terminal like this:
…and also, a failed result from the browser by Karma
The failure is due to the fact that dependencies were not provided or resolved for the User-list component to run successfully.
Fixing User list component test
Let’s fix the failed user list test using the Jasmine test framework. We will deal with the three test case scenarios:
- test that the user list component is created
- test that user data are retrieved successfully when the user component is initialized
- test that error occurs while retrieving users
Update the user-list.component.spec.ts
‘s code to the following:
Let’s understand what’s going on here…
- line #10 - #13
let component...
: declarations of component, fixture, user service, and getUsersSpy. The fixture is a wrapper around the UserListComponent that aids in getting an instance of the component, accessing its template/DOM element via a native or debug element. - line #25 - #31
beforeEach(()=>...
: configure userListComponent and its required dependencies. We specify the name of the component to test in the declaration block (#26). To resolve the UserService dependency, we replace the original user service provider with a fake user service called theuserServiceSpy
instead which is mocked with dummy data in lines, #22 and #23. Also, as the user service makes an HTTP call,HttpClientModule
is imported. - line #33 - #35
fixture = TestBed.create...
: creates a fixture for the user list component. This fixture wrapper makes it possible to get the component’s instance (#34) and its injected service dependency (#35) to be initialized. - line #39
expect(...
: asserts user list component to be truthy. - line #43
component.ngOnInit...
: callsngOnInit()
method on the component thereby triggering the fake user service. This can also be called usingfixture.detectChanges()
to update the DOM. - line #45 - #48
expect(userService...
: verifies that the user service’sgetUsers()
method has been called, number of expected users, and deep object equality check. Moreover, no error message is expected during a successful invocation. - line #52 - #53
const expectedError...
: re-mock getUsersSpy to throw an error.getUsersSpy
is being accessed from line #23 and re-mocked to suit the error case. - line #57 – #59
expect(...
: validates that no user is expected and also an error message is being received.
Notice:
toEqual()
matcher is used for primitives whiletoBe()
is used for both objects and primitives
User list component test result:
User list HTML template testing
Angular application component defines a class that contains application data and logic, and is associated with an HTML template that defines a view to be displayed. The language for templates is HTML, with Angular markup, which separates the view layer.
Sometimes the structure or dynamic data rendered by the HTML element can be different from what needs to be displayed therefore we would have to test the template view page, user-list.component.html
.
The user-list.component.html
file has the following update that will be tested.
Here, we display an error message alert if an error occurs. Additionally, when there’s no data we display “No users available”, otherwise the data is populated on the UI.
There are 5 test cases scenarios for the template. Namely, the user-list page:
- should render the correct page title
- should display an error alert when an error occurs
- should have a table of 4 header fields
- should populate a number of matching records when data is found
- should show a non-availability message for empty data
Now, let’s update the user-list.component.spec.ts
with the template tests:
From line #63, let’s understand what’s going on from the template tests …
- line #65 - #71: updates the DOM with the title then locates the first
<h3>
element and asserts the page to contain the “User List” title. - line #75 - #80: updates the DOM with an error message, locates the alert message element by its CSS class using
debugElement
which wraps thenativeElement
. Finally, it verifies that html element contains an error message. - line #83 — #87: verifies that on template initialization, the table rows should populate 4 records (including the field names) when the mocked user data is available.
- line #90 - #96: similarly, verifies that on ngOnInit(), three mocked user records should be expected. The
slice(1)
(#94) excludes the table field names. - line #98 - #108: the
getUserSpy()
is re-assigned with empty users (#99) and synced with the template (#101). This satisfies the<tr>
element of having the*ngIf="users.length === 0
logic. Then asserts with “no users available” message display (#105). Similarly, verifies that the*ngFor
<tr>
element is not populated and undefined (#108).
After running all the component test suits, you’ll that main
app-component.spec.ts
file’s test fails. To fix them, I have update the tests suit of theapp-component.spec.ts
file.
If the app and user list component tests go well, you’ll receive a successful test pass result like this:
All tests reports…
Combining all tests (service + component + template) and running them, you see a successful result like this:
Conclusion
This brings us to the end of our series for unit testing in Angular. In this episode, we covered component and template testing. Previously, we had also covered setting up an Angular project and testing its services. I really enjoyed writing on these important concepts.
I hope you found this and the rest of the articles useful. In case you have any queries, please feel free to drop a comment, feedback, and question ;).
Additionally, you can find and connect me on LinkedIn.
Project codebase: GitHub link
A big “T” for Thanks!