Using the new bindings API to test Angular components with Angular Testing Library

profile
Tim Deschryver
timdeschryver.dev

Angular Testing Library v18.1.0 adds support for the new bindings functionality in Angular (thank you Suguru Inatomi (@lacolaco)!). Using the methods inputBinding, outputBinding, and twoWayBinding you can now set the component's input and output properties using the official tooling (Angular v20.1).

For those who are currently using Angular Testing Library, you already know that it currently has multiple ways to set the component's properties through componentProperties, componentInputs (deprecated), inputs, componentOutputs (deprecated), and on. These methods were needed to easily set the input and output properties of a component in tests, and evolved over time to support typesafety and signals. With the addition of the new built-in methods in Angular, these use cases are now covered by the Angular framework itself. This will have an impact on the Angular Testing Library API, as the existing methods will be deprecated in future releases in favor of the new bindings property.

Let's take a look at the new bindings property in combination with Angular Testing Library, we'll also cover some cases with just Angular's functionality without bringing in Angular Testing Library.

Example component link

As an example, we use the following component that has a simple input and output property. The component shows the input value using a computed and emits an event when a button is clicked.

Bindings link

To set the component's properties, the render method options now support a bindings property, which is an array of the different binding methods. This is also the case when using the TestBed to create the component, as it also supports a bindings property in the createComponent method options.

Input binding link

To set the input property, you can use the inputBinding method, which can be defined in the bindings property of the render options. The example below sets the name input property using a signal and asserts that the greeting message is displayed.

As you can see, the inputBinding method takes two arguments: the name of the input property and a signal that holds the value of the input property. Instead of using a signal, you can also use an inline method that returns the value.

Input properties that are defined using an alias can also be set using the inputBinding method. In this case, the alias name needs to be used instead of the property name.

In the past, we used aliasedInput in Angular Testing Library to set input properties with an alias, this will also become deprecated in future releases.

Output binding link

To test the output property of a component, you can now use the outputBinding method, which can also be defined in the bindings property of the render options. Just as with the inputBinding method, the outputBinding method takes two arguments: the name of the output property and a callback function that will be called when the output event is emitted.

The second argument is great to pass a spy function, which is ideal to assert that the output event was emitted. This was previously possible, but often required a type assertion to convince TypeScript that the callback function matches the output event type.

Two-way binding link

For properties that require two-way binding (e.g. model), you need to use the twoWayBinding method to set these properties. If the greeting component would have a two-way binding for the name property, it could be set as follows.

In comparison to the inputBinding method, the twoWayBinding method only accepts a signal as the second argument, this is because two-way bindings require a writable value.

Using the standard Angular APIs link

As mentioned earlier, the bindings property is not specific to Angular Testing Library, but is part of the standard Angular testing APIs. This means you're not required to use Angular Testing Library to use the new bindings functionality.

After configuring the TestBed (which is what Angular Testing Library does under the hood), you can create the component using the createComponent method and pass the bindings property in the options. Just as with the render method of Angular Testing Library, the bindings property is an array of the different binding methods.

Some questions I had, and their answers link

Can I use this API with @Input and @Output decorators? link

Yes, you can also make use the inputBinding and outputBinding methods with components that use the @Input and @Output decorators.

What happens if I don't provide a required input? link

If you don't provide a required input, Angular throws an error when the component is initialized.

What happens if I provide an input that doesn't exist? link

One of the advantages of Angular Testing Library's API is that it provides type safety when setting input and output properties. This is not (yet?) the case when using the bindings property, as it accepts strings for the property names. However, Angular does perform runtime checks and will throw an error if you try to set a property that doesn't exist on the component.

Can I update signal values after the component is created? link

Yes, you can update the signal values after the component is created. To assert the template is updated, you need to call fixture.detectChanges() to trigger change detection. Another option, which I prefer, is to use Angular Testing Library's retry utilities, such as findByQuery, which automatically retries until the element is found or a timeout occurs.

Conclusion link

The new bindings API in Angular Testing Library v18.1.0 and Angular v20.1 provides a standardized way to set input and output properties of components in tests.

The new API's offers a better experience with the Angular framework, making it easier to write and maintain tests for Angular components. For example, it has a better integration with Angular's signals, and it reduces the need for type assertions when working with output events. As a result, the existing methods in Angular Testing Library for setting input and output properties will be deprecated in future releases in favor of the new bindings property.

If you haven't tried Angular Testing Library yet, now is a great time to start using it in your Angular projects!

Putting it all together, here's a complete example using Angular Testing Library with the new bindings API:

Feel free to update this blog post on GitHub, thanks in advance!

Join My Newsletter (WIP)

Join my weekly newsletter to receive my latest blog posts and bits, directly in your inbox.

Support me

I appreciate it if you would support me if have you enjoyed this post and found it useful, thank you in advance.

Buy Me a Coffee at ko-fi.com PayPal logo

Share this post