Enhancing our Angular testing experience
Ivy is a major milestone for Angular tests. Besides the stronger typing in tests discussed in Chapter 1, Discovering New APIs and Language Syntax, it adds major speed improvements and useful test utilities, one of which is component testing harnesses, which we will cover in Chapter 4, Exploring Angular Components Features.
In this section, we explore how we can introduce values of unexpected types using a TypeScript annotation, which proves to be useful in tests. After that, we discuss another important aspect of AOT compilation in Angular Ivy.
Expect error compiler annotation
TypeScript version 3.9 introduces a special compiler instruction comment that is useful in tests.
The @ts-expect-error
annotation comment allows values of incompatible types to be passed to functions in the following statement. As an example, let's write an add
function and verify that it rejects strings—even at runtime—for robustness:
function add(left: number, right: number): number { assertIsNumber(left); assertIsNumber(right); return left + right; }
The robust add
function in the previous code snippet applies an assertion function for both operands. Let's test that an error is thrown if strings are passed, as follows:
describe('add', () => { it('rejects a string as left operand', () => { const textInput = '2'; const four = 4; // @ts-expect-error expect(() => add(textInput, four)).toThrow(); }); it('rejects a string as right operand', () => { const three = 3; const textInput = '5'; // @ts-expect-error expect(() => add(three, textInput)).toThrow(); }); });
If we remove the @ts-expect-error
comments, the TypeScript compiler throws errors because of the incompatible values we pass in the tests in the previous code block.
How is this different from @ts-ignore
comments? The @ts-expect-error
comments warn us if a compilation error is not thrown in the statement that follows. This raises our confidence in the code.
Faster tests with AOT compilation
Angular Ivy introduces AOT compilation to tests. This makes the test environment close to the production environment, which is a good trait as it allows us to catch errors early.
Until Ivy, Angular had a long-standing issue of relatively slow tests when they involved component tests using TestBed
. The tests were slow because the test runner was reading, parsing, and compiling one or more files for every component per test case, not per test suite or per test run. Ivy introduces the principle of locality as well as a cache for compiled declarables and Angular modules, which speeds up component tests significantly. Additionally, rebuilds are faster, which improves speed when writing tests and fixing bugs.
With these pieces of valuable information, you now know how Ivy can greatly impact your developer workflow when implementing unit tests. As mentioned in the introduction of this section, we have saved one of the most exciting features for Chapter 4, Exploring Angular Components Features—namely, component testing harnesses.
The next section is all about how Ivy boosts our productivity by improving the Angular developer experience.