If you, like us, enjoy using React for your JavaScript projects, then you probably already use React Testing Library (RTL). You do not? Then it is time you start, because this lightweight solution makes testing easier and faster - the perfect tool in your React toolkit.
For many junior developers, testing can feel like an unnecessary delay - instead of implementing the code they have worked so hard on, time has to be spent running tests and possibly correcting entire sections of code. We are not exempt from that, and many of us are familiar with that feeling of reluctance - but we all learned one way or another that there is no way around testing.
In fact, unit tests are an integral part of the definition of done and simply have to be written.
Raul Bodrea recounts a past project, where, “We always advocated for writing tests but come sprint planning, we found we were too busy implementing features to find time for writing unit tests. It didn’t take long for that project to grow and soon enough we were spending time playing whack a mole - squashing one bug just to spot another.”
Most developers have a similar experience and sooner or later come to the conclusion that spending time writing unit tests makes for faster deliveries and more dependable software. We test because testing ensures the code we write is future-proof, generating fewer legacy issues, running more reliably and integrating more smoothly with later additions and dependencies.
Back in the olden days, developers were using Enzyme to test React apps. This allowed for tests that validate things like component props, HTML element’ attributes and other implementation details that the end user would be blind to. Most web app users are only aware of what is displayed in their browser, and as long as all of the expected components are featured and functional, they are happy.
But developers would often run tests validating information having to do with implementation details rather than actual functionality.
With this in mind, engineer Kent C Dodds came up with a library that, at its core, facilitates testing UI components the same way a normal user would use them. React Testing Library was born. It should be noted that RTL is actually not an original React product - but it has the community’s official seal of approval and is recommended on the official React website.
RTL provides a set of tools that allow developers to find elements and interact with them as a normal user would in a browser, but in a testing environment.
Mounting components
First of all, you will need to set up your unit test, and RTL allows you to do that using the render method. All you need to do is to `import {render} from '@testing-library/react'` and do `const screen = render(<Greeting />)`. As a result, the `<Greeting/>` component will be appended to `document.body`.
You can also pass a second, options parameter to your render call. Among other things, this parameter can help you test components that rely on context providers. In order to do that, you just need to pass the context provider as a `wrapper` in the second parameter. You can find more information on what other options are available here.
Returning to `const screen = render(<Greeting />)`, we have saved the return value of `render` in a constant called `screen`. This is because the `render` function returns:
You can read more about the `render` function’s return value here.
Finding elements
One of the most important prerequisites for front-end component testing is being able to select UI elements. RTL offers the following query sets that allow you to do just that:
How about writing a11y related tests? You can run `ByRole()` queries that allow you to find elements by aria role.
Interacting with elements
Now that you have access to a component, the next step is to actually interact with it. RTL’s companion library, `user-event`, allows you to do just that. All you need to do is to initialise a user by running `const user = userEvent.setup()`.
You can then either use low level events such as `keyboard` or `pointer`, which allow you to simulate various keyboard / pointer interactions with a great deal of control, or use the out of the box helpers such as click, which encompasses a pointer move and a pointer button press event in one command.
Here are a few events you might find helpful:
There are a lot of other events for you to discover here.
Besides helping developers better understand how the UI will be used and what potential flaws the DOM might hide, testing with RTL also prevents needless implementation details from sneaking into the unit/integration tests.
Fewer implementation details in tests translates to more flexibility when it comes to the actual implementation, making the tests much more maintainable and focused on the actual functionality rather than how that functionality is built.
In other words, if a test doesn’t care that a `<p/>` has class “foo” but that it displays “bar”, one could update the structure of the page to have “bar” inside of a `<span/>` and the test would still pass.
Writing and running tests using RTL results in more concise unit tests that are easier to manage and maintain. This is important because, often, fixing tests takes longer than actually writing them in the first place. And we all know that time is money (and sanity).
Working with other frameworks? Following the success and popularity of RTL, other libraries such as Angular Testing Library and Vue Testing Library have been developed to help developers optimise testing.