10 React Testing Library Best Practices
React Testing Library is a great tool for testing React components. Here are 10 best practices to make sure your tests are as effective as possible.
React Testing Library is a great tool for testing React components. Here are 10 best practices to make sure your tests are as effective as possible.
The React Testing Library is a set of helpers that let you test React components without relying on their implementation details. This article covers the 10 best practices that you should follow when using the React Testing Library.
When you test the behavior, your tests will be less likely to break when the implementation changes. For example, if you’re testing that a certain button calls a certain function when clicked, and the implementation of the button changes from a
However, if you’re testing the implementation (for example, testing that the button is rendered as a
This is important because the implementation is likely to change often (for example, when upgrading to a new version of React), but the behavior should remain the same.
If you’re using a library like React Router, then you’ll need to mock out the router context in order to test components that depend on it. By creating a custom render method that mocks the router context, you can avoid having to do this setup in every individual test.
Similarly, if you’re fetching data from an API in your component, you’ll need to mock the API call in order to test the component in isolation. Again, by creating a custom render method that mocks the API call, you can avoid duplicating this setup in every test.
Not only does this make your tests more DRY, but it also makes them more maintainable. If the API call or router context changes in the future, you only need to update the mock in one place, rather than in every test.
When you test implementation details, you are testing the way your code is written, rather than what it does. This means that if you change the way your code is written, your tests will break even though the functionality of your code hasn’t changed.
On the other hand, if you focus on testing the functionality of your code, your tests will still be relevant even if you change the way your code is implemented. This makes your tests more robust and easier to maintain in the long run.
So, how can you avoid testing implementation details? The best way is to use the React Testing Library’s render function instead of shallow rendering. Shallow rendering only renders the component being tested, without its children. This means that any implementation details of the child components will be ignored by the test.
The React Testing Library’s render function, on the other hand, renders the whole React tree, including all child components. This means that implementation details of child components can’t be ignored, and they will be included in the test.
This may seem like a disadvantage at first, but it’s actually a good thing. It forces you to think about the functionality of your code, rather than its implementation. As a result, your tests will be more robust and easier to maintain.
When a test fails because of a UI change, it’s usually for one of two reasons:
1. The test is testing implementation details, rather than the functionality of the component.
2. The test is too tightly coupled to the UI, and even small changes break it.
To avoid these problems, you should write tests that focus on the behavior of the component, rather than its implementation details. For example, instead of testing that a specific button has the correct CSS class, you could test that clicking the button triggers the appropriate action.
You should also make sure your tests are not too tightly coupled to the UI. For example, if you’re testing a form, don’t just assert that the form submission succeeds; also check that the appropriate data is saved to the database. That way, even if the UI changes, your test will still pass as long as the functionality remains the same.
React is a very well-tested library, and part of the reason for that is because the React team only tests the things that they feel are important to test. So if you’re writing a test that’s testing something that React already tests, then you’re effectively testing the same thing twice.
Not only is this a waste of time, but it’s also unnecessary effort. If React breaks the functionality that you’re testing, then your test will break as well. So there’s really no point in testing something that React is already testing.
Of course, there are always exceptions to this rule. For example, if you’re testing a custom React Hook, then you’ll need to write tests for that Hook since React doesn’t do that automatically. But in general, try to avoid writing tests for things that React is already testing.
When you use fireEvent, you’re actually dispatching a real event on the DOM node. This can lead to problems because some DOM nodes might not be able to handle certain types of events. For example, if you try to dispatch a ‘change’ event on an node that doesn’t have a value attribute set, React will throw an error.
Instead, you should use event simulation helpers like change(), click(), and keyDown(). These helpers are provided by the React Testing Library and they allow you to simulate events without actually dispatching them.
When testing asynchronous events, React Testing Library will automatically wait for all async tasks to complete before running the assertions. However, this only works if the async tasks are started within a React event handler. If you start an async task outside of a React event handler (for example, in a setTimeout() callback), React Testing Library won’t be able to detect it and will run the assertions immediately, which is probably not what you want.
To solve this problem, you can wrap your code that starts the async task in an act() call. This will tell React Testing Library to wait for all async tasks to complete before running the assertions.
When you’re testing a React component, it’s important to test the end result of an interaction. For example, if you’re testing a form, you should assert on the state of the form after the user has interacted with it.
This is important for two reasons. First, it ensures that your component is behaving as expected. Second, it helps prevent false positives. If you only assert on the initial state, it’s possible that your test will pass even if the interaction doesn’t work as expected.
For example, suppose you have a form with two fields, username and password. You want to test that when the user submits the form, the username and password are submitted to the server.
If you only assert on the initial state, your test might look like this:
<br>test('form submit', () => {<br> const { getByText } = render(<Form />);</p><!-- /wp:paragraph --><!-- wp:paragraph --><p> fireEvent.click(getByText('Submit'));</p><!-- /wp:paragraph --><!-- wp:paragraph --><p> expect(fetch).toHaveBeenCalledWith('/submit', {<br> method: 'POST',<br> body: JSON.stringify({<br> username: '',<br> password: ''<br> })<br> });<br>});<br>
However, this test will pass even if the form doesn’t actually work. This is because we’re only asserting on the initial state of the form.
To fix this, we need to assert on the final state of the form. We can do this by using the wait
function from the React Testing Library. This function takes a callback, and it will wait until the callback returns a truthy value before continuing.
We can use this to assert on the final state of the form:
`
test(‘form submit’, async () => {
const { getByText } = render(
fireEvent.click(getByText(‘Submit’));
await wait(() => {
expect(fetch).toHaveBeenCalledWith(‘/submit’, {
method: ‘POST’,
body: JSON.stringify({
username
If you have multiple assertions in a single test and one of them fails, the rest of the assertions will not be executed. This can lead to false positives, where a test appears to be passing when it actually isn’t.
It’s also generally a good idea to keep your tests small and focused. By only testing one thing at a time, you can more easily identify which component or functionality is causing a test to fail.
When you’re testing React components, you’re not just testing the component itself, but also the behavior of the child components. So if your test is too complex, it can become difficult to debug and maintain.
It’s also important to keep your tests focused on a single functionality. This way, if the test fails, you know exactly what broke and can fix it quickly.
Finally, remember that React Testing Library is designed to test the UI, not the business logic. So if you find yourself writing a lot of code in your tests, it’s probably a sign that you should move that logic into a separate module.