Making sure you're using the correct query
One of the main focus points of Testing Library is accessibility. We want to make sure tests are easy to write while keeping the user's experience in the first place. Instead of selecting DOM elements via their id attributes or classes, Testing Library is using user-friendly queries.
In the latest versions of the Dom Testing Library, and thus also the Angular Testing Library (version 9), there have been improvements to these queries. One query in particular pops out, the (get|find|query)ByRole
query.
From the docs:
format_quoteThis can be used to query every element that is exposed in the accessibility tree. With the
name
option you can filter the returned elements by their accessible name. This should be your top preference for just about everything. There's not much you can't get with this (if you can't, it's possible your UI is inaccessible). Most often, this will be used with thename
option like so:getByRole('button', {name: /submit/i})
. Check the list of roles.
The short answer to "which query should I use", is most of the time the *ByRole
query. There are some cases that this query will not be able to find the element, luckily Testing Library provides a couple of ways to find the best "fallback" query to use.
Before we take a look at how to find alternatives, let's take a look at other benefits, besides accessibility, that *ByRole
solves.
*ByRole
provides a solution to
link
Querying broken text link
The *ByText
and *ByLabelText
can't find elements that are broken into multiple elements.
For example, given the following HTML, it wasn't possible to query for the text "Hello World".
This can now be solved with the *ByRole
query as:
Finding multiple elements link
There were times that the query I used (mostly GetByText
) resulted in more than one element.
This was causing some of our tests to be brittle, which is what we want to prevent.
For example, to find the login button in the above HTML I would mainly see the following.
This test would fail when a new "login" text would be added to the screen. Not good, as Testing Library aims to write maintainable tests.
A better approach to this problem was to use the selector option.
For this simple case, this approach is OK. But this could cause problems if CSS classes are used as the selector.
Because the *ByRole
query is more explicit, it reduces the chance of finding multiple elements.
Also, if we take a look at the selector option it resembles the *ByRole
query, it's just a poor man's implementation of it. The *ByRole
query is also more robust as it isn't too specific, e.g. it's possible to find heading tags with 'heading'
instead of the h1
to h6
tags.
Tips to find the correct query link
Sadly the *ByRole
isn't a silver bullet.
There are scenarios where the *ByRole
query cannot be used. An example of this is query elements without a role, e.g. a password field. But you don't have to worry, the original queries can still be used and these are also making sure the application is accessible.
To find the query you need, there are several resources to use.
The docs link
The Testing Library website has its own page to guide you which query to use. If you haven't read this, I would recommend it to read it. It's a short summary, but it will definitely help you to write better tests.
Testing Library Playground link
Last week, Stephan Meijer created testing-playground in order to help finding the "best" available query. To use it, copy and paste the markup into the editor and click on the rendered elements or write the queries yourself.
You can also use the browser extension for Chrome or Firefox.
Testing Library configure
method
link
I recently refactored the examples of Angular Testing Library to make use of the *ByRole
query.
I did this by using the newly added option throwSuggestions
to the configure
method.
By default this is turned off and it's possible to turn it on on a global level, or on a per-query level.
As the name implies, it will throw an error if there's a better/safer query is available.
As an example, we can take the following HTML and as a test, a click on the increment button.
Because the throwSuggestions
flag is turned on, we get the following error while we run the test.
Testing Library gives us a better query that we can copy and paste in the test to replace the "bad" query usage.
Simple right? To turn it on or off for a specific query we can do the following, which will have the same result.
Testing Library Messages link
Testing Library provides a useful message when it doesn't find an element for the given *ByRole
query.
See the example below, where every accessible element is logged with its corresponding selector.
More tips for Angular Testing Library link
screen
link
In previous versions of Angular Testing Library, it was only possible to query elements via the queries returned by the render
method. In version 9 of Angular Testing Library, it exports a screen
object which has all the queries available.
This has as benefit that your tests will become less bloated. A second benefit is that it also looks elements outside the component's HTML, this can in particular be helpful if you're using Angular Material because they will add overlays to the body of the document and outside the component tree.
fireEvent
link
Just like screen
, to fire events it's recommended to use the newly added fireEvent
object instead of using the events returned by the render
method. The events fired by fireEvent
will also run a change detection cycle, just like before.
Using find
queries
link
Angular Testing Library version 9, also patches the find
queries and will invoke a change detection cycle before invoking the query. This can be helpful for asynchronous code that impacts the DOM. For example, if a text input is debounced and will render something afterward. Whereas before you had to manually call detectChanges
after a given time.
More tips link
Kent C. Dodds wrote Common mistakes with React Testing Library a couple of weeks ago. Because the tests written for any popular framework or library resemble each other with Testing Library, most of the tips also apply to Angular Testing Library. So, give it a read for more useful tips for writing your tests with Testing Library!
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.