Test the user experience instead.

More often than not I see Angular developers jumping through hoops trying to test the code in components. I see their tests full of spies, perfectly setting up the state of the component to hit certain lines, all in the name of “testing the code”. This cargo cult testing is the wrong way to go about things. Instead you want to forget about the code and start thinking about the user experience.

User experience

User experience, for a component? Yes. Angular developers often forget components manifest as HTML elements. This means they will have some effect on the browser and the user will most likely be able to interact with it. How the user sees and interacts with the component is what’s important. …


This is an abridged (and slightly opinionated) version of TypeScrip’s official handbook .

This is JavaScript

TypeScript may look like C# or Java but make no mistake, this is JavaScript. Anything you can do in JS you can also do it in TS. If you’re coming from an OOP language thinking you can use TypeScript to avoid JavaScript then I got bad news for you.

This reminds me a lot about CoffeeScript. A long time ago (in IT years) a lot of people, myself included, used Coffeescript to avoid JavaScript. That was the bargaining phase on the way to JS. Just accept and embrace it. …


TLDR; Don’t trust code coverage metrics.

I usually start these posts with the use cases for the component or service. This time I’m going to start with an image:

Image for post
Image for post

Do you know how I got 100% coverage?

With a scaffold test file that only checks if Angular can create an instance of the component (generated by SimonTest). Now I wouldn’t say testing the component can load by itself is useless… but close.

If you’ve read any other of my articles you know where I’m going with this…

The goal is to properly test the component, not to achieve 100% coverage!

This is a great example of a component with 100% code coverage yet it only tests that it has a snowball’s chance in hell to load. This isn’t were you want to be so let’s actually test the component. …


TLDR: Use RxJS utility functions like of to mock the observable dependencies. Start with the use cases for the component. Make your tests fail so you know you’re actually testing something.

As usual, let’s start with the use cases for the component. These are the things the user can do with the component. I keep harping on this topic because I often get asked for help either writing or testing a component; but when I ask for the use cases all I get is a description and a mockup of the component.

At that point I often send people back with “call me when you have the list of things the users can do with the component”. More often than not the mere act of writing down the list makes the problem go away. …


TLDR: Use test components sporadically so you can sleep better at night. Using it as the default test strategy makes your test files harder to read and maintain.

I often see test files with components declared just for the sake of testing. Except for very few cases, you don’t need it to get adequate coverage of your use cases (different than code coverage). Your code is already hard enough to maintain without creating extra artifacts to deal with.

The following example is small to make it easy to follow. The concept still applies for larger and more complex components. I have to say this because I often hear “my component is large/complex, I *need* to complicate things further in my tests”. This is similar to the rationalizations for adding boilerplate: “Yeah, it’s overkill for small projects, but it pays off on bigger ones”… No it doesn’t, the boilerplate just gets bigger. …


TLDR: Write tests against use cases, not implementations. Always make your tests fail to make sure they’re actually testing something.

Before writing the tests for the Hero Search Component, let’s start by listing the use cases for the component. These are the things the user can do with it. You write them down before writing the component (I hope) but in this case the component is already written.

Use Cases

  1. Starts with an empty list.
  2. Typing on the input box doesn’t change the list for 299ms.
  3. The list of matching heroes appear after 300ms.
  4. Can perform multiple searches in a row — 300ms apart. …

TLDR

Avoid State. Not that you should avoid it at all costs, but the same practices that lead to more state, are the same practices that makes your code harder to understand, extend, and refactor.

So in general, avoid the following:

  • Global objects
  • Shared variables
  • The keyword let
  • Side effects
  • Impure functions
  • Array mutations
  • Static methods
  • Classes except for DTOs
  • Depending on global interfaces/classes/types
  • Inheritance

The Details

Angular and similar frameworks are inherently stateful and OOP friendly, but that doesn’t mean we can’t move the needle a little bit towards the functional side. The goal isn’t to write 100% functional code (not even close). Instead I want to encourage you to adopt practices which use less state. …


As with every component and test you’ll ever write, it’s best to start with the list of things the component can do.

This is worth repeating. If you only get one thing out of this post, let it be this:

It’s much better to write and test a component starting from a list of things the user can do with the UI, rather than trying to test the code that powers it.

If you do your job right, you’ll either get 100% coverage or find blocks of code which aren’t really needed and thus can be deleted.

In our case the component is created already so we’ll skip that part. The component looks like…


TLDR; We got a test suite of 3K+ tests to run from over 5 mins down to 15 secs with the following optimizations:

  1. Cache Angular testing modules using ng-cache-testing-module. This alone got our test suite to run in 45s.
  2. Clear the CSS styles using a custom jasmine reporter. This cut the execution down to 26s.
  3. Run the tests in parallel using karma-parallel. This got us down to 15s.

The Journey Begins

I don’t know about you but every time I searched for ways to speed up Angular tests I’d always end up in some blog post telling me to run the tests in parallel. …


The dashboard component provides a good opportunity to reinforce the point that you should test what the component does, not how it does it.

TLDR to test the Dashboard component

  1. Import RouterTestingModule to be able to access the router links
  2. Spy on getHeroes from hero service and make it return a set of hero-like objects.
  3. Expect the template to render the links with the right hrefs and names.
  4. Expect the template to render app-hero-search.

See dashboard.component.spec.ts

Longer version

Believe it or not the most important file is the HTML template because it contains the user interface and declares what the user sees and can interact with.

While we can be very specific in the things we want to test, it’s better to leave room for the template to change without affecting our tests. Here we probably want to test the following 3…

About

Simon

Creator of SimonTest, a plugin that generates Angular tests.

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