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. That was our solution to the problem for a while (a band-aid actually), until our build servers started failing with the following error:

WARN [HeadlessChrome 79.0.3945 (Windows 10.0.0)]: Disconnected (0 times) reconnect failed before timeout of HeadlessChrome 79.0.3945 (Windows 10.0.0) ERROR

Disconnectedreconnect failed before timeout of 2000ms (transport error)

Turns out we configured our tests to run in parallel up to half the number of processors in the machine. Since our build servers had one processor the tests were running on a single thread. So it got to the point where we couldn’t run our tests serially!

At that point we knew our days were numbered because it would be a matter of time until running it in X threads wouldn’t be enough. It was already painful to our tests even in parallel.

A Ray of Hope

So we started searching again and we ran across this blog post on Forbes. It gave us the idea to clear the CSS styles and caching the testing modules.

Clearing CSS Styles

Clearing the styles improved our test speeds a little bit so we wanted to keep this optimization but we didn’t want to add a jasmine block to all our tests. There had to be a way to centralize this change. We ended up adding a Jasmine reporter which cleared the styles. We put it in the boot.tests.ts file.

Caching Testing Modules

Time for us to play with ng-bullet. It gave us massive gains but we didn’t like the API and the project seems kind of abandoned. It required us to modify our test files a little too much for our liking. We wanted something we could easily add with find and replace and remove in the future should we ever need to.

That led us to create ng-cache-testing-module. Adding it is a matter of slapping cacheTestingModule() to our test files like so:

Adding it was easy. Just do the following find/replace in VSCode with regex turned on and filtering for *.spec.ts

Find: [\s\S\r]+

Replace: import { cacheTestingModule } from "ng-cache-testing-module";

$0 Find: ^describe\((.*)\{$

Replace: describe($1{

\tcacheTestingModule();

The Chickens Come Home To Roost

Turns out with dozens of developers all our tests weren’t written perfectly neat (shocking I know). Our tests are now flying but we have a few dozen tests which are failing. Not bad, given the situation, if you ask me.

Our problems could be grouped into two categories: sharing and depending on the state of the dependencies and sharing spies.

Shared State

Some of our components depended on stubbed services with state, then some tests changed that state which other tests used. In that case we replaced useValue with useFactory in the providers to make sure our stubs always start the same.

Global Spies

Some of our tests were sharing global spies. In that case we moved the spies to the tests, not the setup.

Let’s Parallelize Again

Now that our tests run in seconds and of course they can run with one thread, it’s time to add the cherry on top: parallelism. We did it with karma-parallel.

Wrapping it up

We updated SimonTest so that as it generates all the boilerplate for our tests, it also caches the testing module using ng-cache-testing-module (if the project is already using it) and use factories for stubs instead of values.

Happy testing.