Angular Unit Testing Series — Episode #3: Component testing w/ Jasmine

Testing component & its HTML template

Erbynn (Erb) John Kwesi
Nerd For Tech
7 min readNov 9, 2021

--

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:

user-list.component.ts

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.

user-list.component.spec.ts

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:

command-line test failure

…and also, a failed result from the browser by Karma

browser test failure report

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:

  1. test that the user list component is created
  2. test that user data are retrieved successfully when the user component is initialized
  3. test that error occurs while retrieving users

Update the user-list.component.spec.ts ‘s code to the following:

user-list.component.spec.ts

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 the userServiceSpy 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... : calls ngOnInit() method on the component thereby triggering the fake user service. This can also be called using fixture.detectChanges() to update the DOM.
  • line #45 - #48 expect(userService... : verifies that the user service’s getUsers() 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 while toBe() is used for both objects and primitives

User list component test result:

user-list component test report

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:

  1. should render the correct page title
  2. should display an error alert when an error occurs
  3. should have a table of 4 header fields
  4. should populate a number of matching records when data is found
  5. 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 the nativeElement. 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 the app-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!

--

--