Improving Angular tests by enabling Angular testing module teardown

Cover photo by Marian Kroell on Unsplash.

The destroyAfterEach Angular testing module teardown option addresses several long-time issues when using the Angular testbed:

The host element is not removed from the DOM until another component fixture is …


This content originally appeared on DEV Community and was authored by Lars Gyrup Brink Nielsen

Cover photo by Marian Kroell on Unsplash.

The destroyAfterEach Angular testing module teardown option addresses several long-time issues when using the Angular testbed:

  • The host element is not removed from the DOM until another component fixture is created
  • Component styles are never removed from the DOM
  • Application-wide services are never destroyed
  • Feature-level services using the any provider scope are never destroyed
  • Angular modules are never destroyed
  • Components are destroyed 1 time less than the number of tests
  • Component-level services are destroyed 1 time less than the number of tests

The two first issues have the biggest impact when using Karma which runs the component tests in a browser.

Did you know? Angular modules and services support hooking into the OnDestroy lifecycle moment by implementing an ngOnDestroy method.

In this guide, we:

  • Explore the ModuleTeardownOptions#destroyAfterEach option for the Angular testbed
  • List full Angular testing module teardown configurations for Karma and Jest for reference
  • Examine how to opt in or opt out of Angular testing module teardown in a test suite or test case
  • Discuss caveats and remaining issues with the Angular testing module

Exploring the destroyAfterEach Angular testing module teardown option

Angular version 12.1 adds the teardown option object ModuleTeardownOptions which can be passed to TestBed.configureTestingModule for a test case or to TestBed.initTestEnvironment as a global setting.

We can enable the destroyAfterEach option as part of the teardown option object. This in turn enables the rethrowErrors option which is not covered by this guide.

In Angular versions 12.1 and 12.2, ModuleTeardownOptions#destroyAfterEach has a default value of false. In Angular version 13.0 and later, its default value is true.

When destroyAfterEach is enabled, the following happens after each test case or when testing module teardown is otherwise triggered:

  • The host element is removed from the DOM
  • Component styles are removed from the DOM
  • Application-wide services are destroyed
  • Feature-level services using the any provider scope are destroyed
  • Angular modules are destroyed
  • Components are destroyed
  • Component-level services are destroyed

Angular testing gotcha: Platform-level services are never destroyed in Angular tests.

Angular testing teardown triggers

The following events trigger Angular testing teardown when destroyAfterEach is enabled:

  • TestBed.resetTestEnvironment is called
  • TestBed.resetTestingModule is called
  • A test case finishes

Next, let's look at full configuration examples for the Karma and Jest test runners.

Enabling Angular testing module teardown in Karma

Until Angular version 12.1 (inclusive) and in Angular 13.0 and later versions, a generated main Karma test file (test.ts) looks as follows:

// This file is required by karma.conf.js and loads recursively all the .spec and framework files

import 'zone.js/dist/zone';

import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing';

declare const require: any;

// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
test.ts generated by Angular version 12.1 and 13.0

Angular version 12.1 adds a 3rd parameter to TestBed.initTestEnvironment as seen in the following snippet generated by Angular version 12.2:

// This file is required by karma.conf.js and loads recursively all the .spec and framework files

import 'zone.js/dist/zone';

import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing';

declare const require: any;

// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting(),
  { teardown: { destroyAfterEach: true } }, // 👈
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
test.ts generated by Angular version 12.2

For reference, TestBed.configureTestingModule also accepts a teardown option in Angular 12.1 and later versions as seen in this snippet:

TestBed.configureTestingModule({
  teardown: { destroyAfterEach: true }, // 👈
  // (...)
});
Test suite setup enabling Angular testing module teardown

Enabling Angular testing module teardown in Jest

If our workspace or project is using Jest for unit tests, test-setup.ts files probably look as follows:

import 'jest-preset-angular/setup-jest';
test-setup.ts with Angular preset for Jest

To enable Angular testing module teardown in Angular versions 12.1 and 12.2, use the following code:

import 'jest-preset-angular/setup-jest';
import { getTestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';

getTestBed().resetTestEnvironment();
getTestBed().initTestEnvironment(
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting(),
  { teardown: { destroyAfterEach: true } }, // 👈
);
test-setup.ts for Jest with Angular testing module teardown

The Angular preset for Jest already initializes the Angular testbed environment so we have to reset it before configuring and initializing the Angular testbed environment.

With enabling Angular testing module teardown globally covered, let's move on to opting out of Angular testing module teardown.

Disabling Angular testing module teardown

If our Angular tests break after enabling Angular testing module teardown, we can opt out globally or locally.

We might want to opt out because various Angular testing libraries might break when destroyAfterEach is enabled or they might not accept or specify this option.

Use the following snippet to opt out of Angular testing module teardown in an entire test suite:

import { TestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';

beforeAll(() => {
  TestBed.resetTestEnvironment();
  TestBed.initTestEnvironment(
    BrowserDynamicTestingModule,
    platformBrowserDynamicTesting(),
    { teardown: { destroyAfterEach: false } }, // 👈
  );
});

Use the following snippet to opt out of Angular testing module teardown in one or multiple test cases

import { TestBed } from '@angular/core/testing';

beforeEach(() => {
  TestBed.configureTestingModule({
    teardown: { destroyAfterEach: false }, // 👈
    // (...)
  });
});

If a component fixture has already been created, we must call TestBed.resetTestingModule before TestBed.configureTestingModule.

Finally, it's possible to opt out of Angular testing module teardown across our entire workspace by applying the optional Angular migration named migration-v13-testbed-teardown using the following command:

ng update @angular/cli^13 --migrate-only=migration-v13-testbed-teardown

Conclusion

When Angular testing module teardown is enabled by setting ModuleTeardownOptions#destroyAfterEach to true, the Angular testbed manages resources between test case runs by triggering the OnDestroy lifecycle moment for:

  • Application-level services
  • Feature-level services
  • Angular modules
  • Components
  • Component-level services

However, the ngOnDestroy hook of platform-level services is never triggered between tests.

Host elements and component styles are removed from the DOM which is especially important when using Karma which runs tests in a browser.

This all happens when TestBed.resetTestEnvironment or TestBed.resetTestingModule is called or at the latest when a test case finishes.

We discussed how ModuleTeardownOptions were introduced by Angular version 12.1 but that schematics-generated values and default values changed in Angular versions 12.2 and 13.0 as seen in the following table:

Angular version Default value of destroyAfterEach Schematics-generated value for destroyAfterEach
<=12.0 N/A N/A
12.1 false N/A
12.2 false true
>=13.0 true N/A

In the sections Enabling Angular testing module teardown in Karma and Enabling Angular testing module teardown in Jest, we referenced full sample global Angular testing module teardown configurations for both the Karma and Jest test runners.

We learnt how we can opt out of Angular testing module teardown on a global level by calling TestBed.resetTestEnvironment followed by TestBed.initTestEnvironment, specifying the teardown option with destroyAfterEach set to false.

We discussed how to opt out of Angular testing module teardown on one or more test cases by passing a teardown option object with destroyAfterEach set to false to TestBed.configureTestinModule, optionally preceded by a call to TestBed.resetTestingModule.

Additionally, we learnt how to apply the migration-v13-testbed-teardown migration to opt out of Angular testing module teardown across our entire workspace.

Resources

Findings in this guide are based on the following Angular pull requests:

I wrote a few hundred tests to compare initialization and teardown behavior when ModuleTeardownOptions#destroyAfterEach is enabled and disabled. If you're curious, they're available at github/LayZeeDK/angular-module-teardown-options.


This content originally appeared on DEV Community and was authored by Lars Gyrup Brink Nielsen


Print Share Comment Cite Upload Translate Updates
APA

Lars Gyrup Brink Nielsen | Sciencx (2021-10-13T22:22:58+00:00) Improving Angular tests by enabling Angular testing module teardown. Retrieved from https://www.scien.cx/2021/10/13/improving-angular-tests-by-enabling-angular-testing-module-teardown/

MLA
" » Improving Angular tests by enabling Angular testing module teardown." Lars Gyrup Brink Nielsen | Sciencx - Wednesday October 13, 2021, https://www.scien.cx/2021/10/13/improving-angular-tests-by-enabling-angular-testing-module-teardown/
HARVARD
Lars Gyrup Brink Nielsen | Sciencx Wednesday October 13, 2021 » Improving Angular tests by enabling Angular testing module teardown., viewed ,<https://www.scien.cx/2021/10/13/improving-angular-tests-by-enabling-angular-testing-module-teardown/>
VANCOUVER
Lars Gyrup Brink Nielsen | Sciencx - » Improving Angular tests by enabling Angular testing module teardown. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/10/13/improving-angular-tests-by-enabling-angular-testing-module-teardown/
CHICAGO
" » Improving Angular tests by enabling Angular testing module teardown." Lars Gyrup Brink Nielsen | Sciencx - Accessed . https://www.scien.cx/2021/10/13/improving-angular-tests-by-enabling-angular-testing-module-teardown/
IEEE
" » Improving Angular tests by enabling Angular testing module teardown." Lars Gyrup Brink Nielsen | Sciencx [Online]. Available: https://www.scien.cx/2021/10/13/improving-angular-tests-by-enabling-angular-testing-module-teardown/. [Accessed: ]
rf:citation
» Improving Angular tests by enabling Angular testing module teardown | Lars Gyrup Brink Nielsen | Sciencx | https://www.scien.cx/2021/10/13/improving-angular-tests-by-enabling-angular-testing-module-teardown/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.