Speed up Angular unit test duration

-

Unit tests are the first line of defense for developers when working on a large application. With a large application, comes great responsibility. The number of unit tests can quickly exploded into hundreds, or even thousands. These tests need to run before each build. It can become a real pain when the unit test duration becomes high.

There is a flaw in Angular TestBed, which makes it even more painful. So let me enlighten you, and point out the issue I’ve addressed and fixed for some projects I’ve been working on, which reduced the duration of running unit test by threefold.

Component style references

During a unit test, when importing and compiling a component, styles are added to the head section of the iframe where the test is running. The problem is that the styles after the test aren’t removed from the head. Since the head will always load before the DOM is ready, it will slow down the spec runner. This isn’t that big of a deal when there aren’t a lot of tests. But imagine when there are 200 components.

Improve unit test duration

Instead of imagining, I’ve created an empty project using the Angular CLI tool, and copied the spec file of the app component 200 times. Each spec file contains 3 tests, so it’s a total of 600. When running ng test, the duration of running all tests takes up to +/- 20 seconds. Not too bad, 20 seconds isn’t that much. But when you clear the style sheet after each test, it can speed up almost 3 times!

Unit test duration before improvements

When adding the following code to your test.ts file within the src directory, every injected style rule will be removed after each describe block.

jasmine.getEnv().afterEach(() => cleanStylesFromDOM());

const cleanStylesFromDOM = () => {
  const head = document.getElementsByTagName('head')[0];
  const styles = head.getElementsByTagName('style');

  Array
    .from(styles)
    .forEach(style => head.removeChild(style));
};

This is the result after running the same set again, with clearing the styles after each test:

Unit test duration after improvements

The duration of the spec runner went from 20.682 tot 7.884 seconds!

Current state

This issue has been reported to the Angular team in July 2019. But till today a fix hasn’t been merged. In the description of this issue, the real problem is addressed.

Angular appends inline styles associated with the component via the SharedStylesHost service. These inline styles are cleaned up when the service is destroyed.

Unfortunately, these inline styles are never cleaned up in Karma unit tests. This appears to be because each SharedStylesHost service is never destroyed, apparently because each associated NgModule is never destroyed between specs.

Update

If force cleaning all the styles in the head section is causing problems, you could try to only remove styles added by the shared styles host service. In order to do so, the service needs to be destroyed after each test.

jasmine.getEnv().afterEach(() => getTestBed().inject(ɵDomSharedStylesHost).ngOnDestroy());

Enjoy all the spare time you and your team gain from this!