Cover photo by Science in HD on Unsplash.

Original publication date: 2020-01-27.

The Angular Ivy version 9 release introduces substantial testing enhancements and new features. For those passionate about testing, Angular version 9 delivers a satisfying experience:

  • AOT compilation in tests
  • Faster builds
  • Faster tests
  • Stronger typing
  • A new concept called component harnesses

AOT compilation everywhere

It's true. With Ivy, AOT compilation is now the default across all project stages, including the testing phase. This means errors surface earlier because the test environment aligns more closely with production.

Faster builds and rebuilds with Ivy

Compared to the View Engine compiler, the Ivy compiler is generally faster. In Angular version 9, Ivy is the default choice, and this naturally extends to test compilation.

The combination of the locality principle and a caching system leads to quicker rebuilds. This is another substantial advantage that positively affects the testing workflow.

Faster tests with Ivy

Since Angular version 2 was first launched in September 2016, unit tests have faced a significant performance bottleneck. Recompilation of all components occurred between each test case (each it declaration). This problem was more pronounced for components with separate template files and stylesheets, as multiple disk reads were needed for each component.

This changes starting in Angular version 9. The Angular TestBed now stores compiled declarables and Angular modules in its cache between test cases, resulting in significant speed increases for all component tests that utilize the TestBed.

Stronger typing for dependency injection in tests

The deprecation of TestBed.get in Angular version 8 sets the stage for its replacement in version 9: the type-safe TestBed.inject<T>.

Two key distinctions separate TestBed.get from TestBed.inject<T>:

  1. While TestBed.get provides an any return type, TestBed.inject<T> returns a value of type T.
  2. The token accepted by TestBed.get is of type any, but TestBed.inject<T> requires a token of type Type<T> | InjectionToken<T> | AbstractType<T>.

In (1), the type T can be a concrete class, an abstract class, or the value produced by a dependency injection token, as determined by the token argument supplied.

Point (2) mirrors a historical situation with Injector#get, which accepted an any token in Angular version 2. This older signature was deprecated in version 4 in favor of a method signature comparable to TestBed.inject.

Practically, this means tokens such as strings or numbers can still be used as injector tokens. However, this approach has been deprecated for three years and is not recommended.

For our tests, the practical impact of TestBed.inject is that TypeScript can now automatically determine the type of the resolved value, as illustrated in Listing 1.

// my.service.spec.ts
it('infers dependency types', () => {
  // `service` has inferred type `MyService` in Angular version 9
  const service = TestBed.inject(MyService);
});
Enter fullscreen mode Exit fullscreen mode

Listing 1. The types of resolved dependencies are now inferred.

Angular CDK introduces component harnesses

Angular CDK version 9 comes with a new testing subpackage designed for implementing and utilizing component harnesses in tests.

These harnesses serve as an abstraction layer, concealing a component's internal structure while offering an API to access vital DOM attributes like ARIA properties, simulate user interactions without direct DOM queries, and retrieve harnesses for associated components, including children, dialogs, or menus opened by the current component.

Component harnesses are versatile, suitable for unit, integration, and end-to-end tests. Angular CDK includes two ready-made harness environments:

  • TestbedHarnessEnvironment for unit and integration tests
  • ProtractorHarnessEnvironment for Protractor-driven end-to-end tests

While primarily intended for the conventional Jasmine and Karma testing setup, the TestbedHarnessEnvironment is compatible with other runners and frameworks. I've personally verified its functionality using Jest.

Should the standard environments not fit your testing stack, Angular CDK allows you to build a custom harness environment.

Learn how to create your own component harnesses in "Create a component harness for your tests with Angular CDK".

Angular Material adds component harnesses

Angular Material version 9 provides component harnesses for its own set of components. You can now use these components in your tests without being tied to their DOM structure or data binding specifics.

Offering these harnesses gives the Angular Components team the flexibility to modify DOM implementations without jeopardizing your component tests. This is a planned change, as they are in the process of swapping out Angular-native implementations for adapters from Material Component for the web, a separate Google project.

Learn how to use Angular Material's component harnesses in "Create a component harness for your tests with Angular CDK".

Component harness benefits

  • Test as a user
  • Hide implementation details from tests
  • Use the same harness for all types of tests (unit, integration, end-to-end)
  • Publish component harnesses with our Angular libraries
  • Use the published component harnesses for internal Angular library tests
  • Use 3rd party component harnesses to exercise 3rd party Angular components without depending on their implementation details
  • Automatically trigger change detection between component interactions
  • We don't have to add separate attributes or classes for test selectors since all tests share the single selector defined by the component harness

Conclusion

Angular version 9 marks a remarkable milestone for testing!

Ivy brings AOT compilation to every corner of the development process, accelerates build and rebuild times, speeds up test execution, and enhances type safety for dependency injection.

Component harnesses present an intriguing testing methodology. Their test-as-a-user APIs work seamlessly across unit, integration, and end-to-end test scenarios.

Angular Material leads the way as the first Angular library to ship component harnesses for its components.

Learn how to use component harnesses, how to implement component harnesses and how to implement custom harness environments in the official component harness guide.

Learn how to use Angular Material’s component harnesses in your tests in the official Angular Material guide.

Peer reviewers