Simpler Unit Testing for Angular Components

Krishna Chaitanya Acondy
The Startup
Published in
4 min readMay 23, 2019
If you wish to make an apple pie from scratch, you must first invent the universe. — Carl Sagan

I’ve always found the setup of Angular component unit tests rather complicated. This is especially a pain point when the component is high up in the hierarchy, and includes one or more child components, each of which may have children of their own. What makes it worse, when any of these components injects any dependencies using the Angular DI mechanism, you’re left wondering about all the modules and providers that you need to define in your test module to be able to test one single component!

This reminds me of the famous Carl Sagan quote:

If you wish to make an apple pie from scratch, you must first invent the universe.

I recently started working on a new Angular project at work, and spent some time scaffolding out the project set up. I wanted to set up a pattern which simplified unit testing of components, so we could remove the entry barrier for unit testing, and make it possible for our developers to use the TDD approach.

To better understand the problem, I started by creating a simple app with a TodosComponent that displays a list of items that it gets from the TodoService. To be able to unit test the TodosComponent, I would need to set up a test module that includes the TodosComponent, and a mocked out version of the TodoService, so I can test the component in isolation. The boilerplate to set that up looks like this:

So far so good. Now, imagine if we needed to include the todo list in a DashboardComponent that displays other information. The test module for the DashboardComponent would need to include everything required to instantiate the TodosComponent, in addition to its own needs.

In the case of this very simple example, there’s just one higher level, the AppComponent, which would then need to include all the dependencies of the TodosComponent, DashboardComponent, and itself.

Real-world applications, however, tend to look more like this:

And by the time you’re done rooting around in test modules and dependency trees, you’re probably ready for this:

So, how can we simplify this process and ease the pain? I was able to come up with two solutions:

1. The Test Module Pattern 📦

In the above example, the TodosComponent and TodoService are packaged into the TodosModule, which looks like this:

If we created a TodosTestModule which provided a mock version of the TodoService, all we would need to do in our unit test is just import that module.

The great thing about this, is that if we create a DashboardTestModule for the dashboard feature, we can reuse the TodosTestModule by importing it straight in, and that goes for any module higher up!

That significantly simplifies dependency management for unit tests by providing a simple pattern that is easy to understand, and promotes reuse.

We can take this a step further and abstract some code into helper functions that can be used across the test suite.

2. Test Helpers 👨‍🔬👩‍🔬

With the way we have set up test modules, the building up of the test module has now become a simple task, and as a result, I started to see code repeating itself in beforeEach blocks across the test suite. The only difference in these blocks was the module being imported in.

So I created a setupTestModule<T> function that takes a module type and an optional set of providers(just in case you need to override the providers for a particular set of tests).

In addition, I created a type TestHarness, which contains the test fixture and component being tested. A createTestHarness<T> factory function creates the test fixture, instantiates the component, runs detectChanges, and returns an instance of TestHarness back.

So our spec for the TodosComponent has now been simplified to this:

So there you have it — Working, maintainable unit tests easy as apple pie 🥧, and you didn’t even have to invent the whole universe for it! 🙌

All the code discussed above is available on Github.

Next Steps

I would like to flesh out the TestHarness class more fully, to be able to do things like querying the DOM of the component under test using fixture.debugElement and so on.

I would also like to explore how to use schematics to generate test modules, and a custom template for unit tests based on the stuff described above.

Please give me a shout if you’re interested in collaborating on any of these, or other ways to make this better!

--

--