An Opinionated Approach to Testing Angular
Tags: angular, javascript, testing, jest
Published: 2019-07-22 05:56:52

---
title: An Opinionated Approach to Testing Angular
published: true
tags: angular,javascript,testing,jest
canonical_url: 
---

![](https://cdn-images-1.medium.com/max/1024/1*biRyJxXPSuGSXADNxgyhQw.png)<figcaption>Angular plus Jest. Logos from <a href="https://worldvectorlogo.com/logo/angular-icon">https://worldvectorlogo.com/logo/angular-icon</a> <a href="https://freebiesupply.com/logos/jest-logo/">https://freebiesupply.com/logos/jest-logo/</a></figcaption>

#### Informed by a few years of writing, reading and depending on tests.

##### Originally posted at https://medium.com/ng-gotchas/an-opinionated-approach-to-testing-angular-4cf14ef7463f

First, let me lay out the **basic points** and then we’ll jump into  **details**.

> Disclaimer: I will not try to convince you that unit testing is a good idea. If you don’t think it is, go read something else. The opinions expressed here may seem arbitrary and just plain wrong if you are not relying on unit tests for your everyday work.

**1. No logic in the templates ** — just call an event **handler** public **method**. We can **trust Angular** to do its part (that is already tested — [example](https://github.com/angular/angular/blob/master/packages/common/test/directives/ng_if_spec.ts)) which is to call us. And **we** do our part —  **test** only **our code**.

**2.** Include **all dependencies** as **injectable** constructor params. Document, Date.now(), requestAnimationFrame etc.

**3.** All **properties** and **methods** used by the template are **public **. This might be obvious to you but it was not for me initially. And the [language service](https://marketplace.visualstudio.com/items?itemName=Angular.ng-template) wasn’t there to tell me otherwise.

**4.** In **each test** have a setup function that sets up all required state and infrastructure for the test.

**5.** In the setup function use the **builder pattern** to **set up** the **dependencies** and **construct** the class under test.

**6.** Use Jest ([jest.io](https://jestjs.io/)) [Jest’s snapshot feature (this one is marginally useful)]

**7.** For button clicking and input typing i.e. what the user uses the UI for, create a few UI (aka end-to-end) tests which actually use the user interface

All right — now for some details:

**1. No logic** in the **templates ** — just call the event handler public method — this way we can trust Angular to do its part (which is already tested [example](https://github.com/angular/angular/blob/master/packages/common/test/directives/ng_if_spec.ts)). **We test** only **our code**. Consider the following two versions of the same template:

{% gist https://gist.github.com/gparlakov/11770665cec62b44dd8ca4c9d593248a %}

The top one looks like a smart and readable choice when writing the template but **actually** requires for your **test** to **use** the **template** , which needs to get compiled etc. It also requires any person reading the code to have both the my.component.html and my.component.ts open in order to follow the logic.   
The **bottom** one allows for the test to **instantiate** the component class (no compiling of the template and so on) and **simply call** the public **method** onHereButtonClick(). The name of the handler is a **convention** combining the element name and the event name — onHereButtonClick — when here button is clicked do something — the logic.

{% gist https://gist.github.com/gparlakov/0401ec49d9c56e690c7b0404bf50fc00 %}

This way the my.component.ts holds the logic and not the template and a person reading it can follow the logic that much easier.

> …trusting that the DOM and/or Angular will call my code and when I call them they will do their job…

**1.1.** That allows us to only test the class without compiling the whole component with its .css and .html. Much faster, easier and no need to keep all the .ts and .html and .spec.ts open in order to write the tests

2. Include all **dependencies** as **injectable** constructor params. Document, Date.now(), requestAnimationFrame etc.  
Any direct calls to browser APIs like Date.now() or requestAnimationFrame or setTimeout become hard to test. But, if we wrap those in services the tests become trivial.

{% gist https://gist.github.com/gparlakov/768f04e1bac4335989af27477b309b2e %}

{% gist https://gist.github.com/gparlakov/5772cd199403e413e2236888997ebcca %}

The **document** is already provided by the framework: [https://github.com/angular/angular/blob/2b44be984e0932bab83fcea015fc5b4ee2d3b151/packages/common/src/dom\_tokens.ts#L11-L19](https://github.com/angular/angular/blob/2b44be984e0932bab83fcea015fc5b4ee2d3b151/packages/common/src/dom_tokens.ts#L11-L19)

**2.1.** Use the AAA — [arrange act assert](https://medium.com/@pjbgf/title-testing-code-ocd-and-the-aaa-pattern-df453975ab80)pattern/convention. It allows for the person reading the code to **discern what** happens **where**.   
Any similar convention —ex When Then Should — would do the trick as long as the whole team is on board of course.

3. All **properties** and **methods** used by the template are **public**  . This might be **obvious** to you but it was not for me initially. And the [language service](https://marketplace.visualstudio.com/items?itemName=Angular.ng-template) wasn’t there to tell me otherwise. Since the method needs to be used by the template, which is ‘external’ to the class, it needs to be public. That’s how I think about it. Also in the test that allows us to decide what we need to test — any public method.

4. In each test have a setup function that sets up all required state and infrastructure for the test.  
Instead of relying on the TestBed we shift its responsibilities to a setup function. We call that function in each test (or in beforeEach hook) and it returns to us an object that allows for specifying what is relevant to this test and constructing the class-under-test. In case of changes in the dependencies or the public API of the class (due to everchanging requirements), we’ll be able to update the test in a single place — the setup function.

{% gist https://gist.github.com/gparlakov/50b318cc8b60526491f8163fd64d0164 %}

**4.1** Use autoSpy. That’s a function that can create an object with the methods of the class **mocked automatically**.

{% gist https://gist.github.com/gparlakov/73f5b82094cdd680b5f76fb7842b0c6e %}

It allows the returned object to get used like:expect(o.method).hasBeenCalledWith('arg1'); and o.method.mockReturnValue(10).

This is a simple implementation. If a service needs to return a complex object you need to take the mock and add that behavior. See the example in the next section (5.) withTempUser method on the builder object.

A similar idea is implemented in the [jasmine-auto-spies](https://github.com/hirezio/jasmine-auto-spies).

5. In the setup function use the **builder pattern** to **set up** the **dependencies** and **construct** the class under test. The builder pattern allows for dot-chain declaring of each particular test’s needs.

In the following example, we need to have a temporary user on our site which only stores their email in their localStorage . Only tested one of the methods for conciseness.

{% gist https://gist.github.com/gparlakov/700e2bbb4094145b6d00534047b602c8 %}

In the builder we have the withTempUser method that allows us to specify that there is a temp user with a certain email. That takes the messy mock code and:

- **moves** it in one place, out of the test for improved test **readability** and **maintainability**
- **preserves** the **intention** for the person reading so they can see what we wanted to have happen, instead of trying to guess by reading the implementation

For each **business** logic **case** , we can add a similar **method** that does a piece of dependency or state **set up**.

**5.1.** That (the setup function implementing the builder pattern) also allows for some automation. Basically, a tool that can read the class-under-test and create a test for it following these guidelines. And update that test when the class-under-test changes. See [SCURI](https://www.npmjs.com/package/scuri).

**6. Use Jest**. It has (almost) **identical API** to **Jasmine** so migrating existing **tests** and testing **skills** is trivial. Jest has a bit **better errors test** than jasmine, like showing you exactly **what** is the **difference** between the expected and the actual value in a failed test. It does not **depend** on starting a **server** and running a **browser.** Instead, it uses the [JsDOM](https://github.com/jsdom/jsdom)library and everything runs in a node.js process. It **parallelizes** the tests using all available CPU cores. It supports [mocking js modules](https://jestjs.io/docs/en/mock-functions.html#mocking-modules) so that your tests see the mocked ../my-service.js instead of the actual ../my-service.js Don’t rely on this for all unit tests, but this feature saved our butts on a few occasions.  
Finally — for our use case at [propy.com](https://propy.com)— we could not start Chrome on our build machine and that was the main turning point to switch to our Angular unit tests using Jest.   
**Switching** to Jest is even simpler when using this [Jest Schematic](https://github.com/briebug/jest-schematic).  
Finally, Jest has a nice console UI when running in jest --watch mode. Lets you filter test by file name, test name, status failed or just changed i.e. understands git changes and runs only the currently changed files — which makes a lot of sense.

![](https://cdn-images-1.medium.com/max/1024/0*PwUQseev7g-5gWFY.png)<figcaption>An error report from Jest. From the jestjs.io site.</figcaption>

Using Jest’s **snapshot** feature proves to be **marginally** useful and a **major pain** to maintain. For the 6 months we used it we had maybe 1 or 2 instances that it may be caught something. That is — during a code review, I noticed that after changing something in a component a whole section disappeared from its rendered HTML (which is what we did a snapshot on). I am now not doing snapshots on the new components I write.

See this great article for a stronger opinion on that [https://medium.com/@tomgold\_48918/why-i-stopped-using-snapshot-testing-with-jest-3279fe41ffb2](https://medium.com/@tomgold_48918/why-i-stopped-using-snapshot-testing-with-jest-3279fe41ffb2)

7. For **testing** the actual **UI** , like button clicking and input typing (what the user experiences) create UI (aka end-to-end) tests that actually exercise the UI as a user would. I use **Puppeteer** ( [https://pptr.dev/](https://pptr.dev/)) because it’s developer friendly, but have also used Protractor ( [https://www.protractortest.org/#/](https://www.protractortest.org/#/)) and heard good things about **Cypress** ( [https://www.cypress.io/](https://www.cypress.io/))

Bonus thoughts:  
Unit tests that do **button clicking** and **event dispatching** I find expensive to write and very **low** return on investment ( **ROI** ). I tend to not write any of those. Instead, I control the instantiation of the class-under-test (component, directive, service) via the setup builder then invoke the public methods and examine the results, **trusting** that the **DOM** and/or **Angular** will call my code what I declared I wanted it called and when I call them— they will do their job.

They are expensive to write because I need to uncover the underlying DOM calls, usually hidden by Angular (e.g. dispatchEvent, addClass, focus) all the while Angular actually provides that functionality and has tested it thoroughly.

Hey, thanks for reading and happy to discuss — how do you do your testing?

* * *