How to unit test your React App

While writing React code, it is important to ensure its quality and reliability. One key aspect of achieving this is through unit testing, a technique where individual units of code are tested to ensure that they perform as expected. In the context of a React application, this means testing individual components to ensure that they behave correctly under different scenarios. Unit testing is a technique where individual units of code are tested to ensure that they perform as expected. In the context of a React application, this means testing individual components to ensure that they behave correctly under different scenarios.

Table of content

  1. Why unit testing?

  2. Tools

  3. Testing React App

    1. Simple functions

    2. Basic components

    3. Components + props

    4. Component with multiple elements

    5. Event handler testing

    6. Stateful components

    7. Testing simple custom hook

  4. Some of the rules of unit tests

  5. What about integration and E2E testing?

Why unit testing your React App?

Writing unit tests for React components allows developers to find bugs and errors early in the development cycle, thereby saving time and money. By ensuring that the code meets the functional specifications and business logic of the application, unit testing can help improve the code's overall quality. Additionally, unit tests provide a safety net while modifying the code, enabling programmers to locate and resolve problems as they arise.

Pros of unit testing:

  • catching errors early

  • fastest tests to run

  • reduced time and cost of development in the long run

  • refactoring is easier (safety net)

  • deployment is easier and faster (catch errors before they make it to production)

  • better documentation

Tools for unit testing react apps

I created a project using create-react-app so testing libraries are already installed, but as it's getting deprecated and you might check Vitest documentation while using Vite to create and test your app.

https://vitest.dev/

Tools I will use:

Testing React App

Simple functions

The most common usage for unit tests is testing functions. First, there are 2 important keywords for unit testing: describe and test. Test is a function accepting two arguments, the name of the test and a callback with our assertion. Describe is a function that accepts also 2 arguments, the name of the test group and a callback with tests. Describe is used to group and organize your tests in one place.

Basic components

Most often in the context of React Apps, we will unit test individual components. Let's start with writing a very simple App component and then test it in App.test.tsx.

In our App.test.tsx and then in the terminal run npm run test

In most of React component testing, we will import and use render and screen from RTL. First is used to render components, keep in mind that when that component uses for example a redux store you should pass its provider to your rendered component as props. The screen is used to query elements from your component. There are different types of queries while testing such as getBy, findBy, getAllBy and more. We can query by for example text, placeholder text or role. To find out more about those methods here is a link to RTL Queries docs.

https://testing-library.com/docs/queries/about/

Also, quite a good pattern of writing your tests is Arrange-Act-Assert. BDD (Behavior-Driven Development) follows this pattern by another name: Given-When-Then, but both behave similarly.

  1. Arrange - we set up our test case and in the context of testing React, we most often render our components, but not only, as we can create a mock object or API our test requires

  2. Act - those are actions focused on behavior and cover the main thing to be tested, often calling function, REST API

  3. Assert - used to determine if the test passed or failed, sometimes they are simple like checking values, but sometimes asserting might be more complex for example checking a URL params

Components with props

Let's test a more interactive yet still simple component that accepts name as props.

In our Dog.test.tsx:

The test is still fairly straightforward, while rendering the component we have to pass all props it requires, then we get it by text and expect it to be in the document. Let's tweak it a bit and add a role to our div in component role="contentinfo" and then it's possible to get our dog not by text but by role.

And our tests passed :)

Components with multiple elements

To show you how to select multiple elements in test cases, I have created Dogs.tsx component which accepts and renders array of Dog objects.

In Dogs.test.tsx:

As said before Arrange part of the test might include not only the rendering component but also creating objects. Take a look at our query, it's right now getAllBy, not getBy, meaning we get multiple elements with that role, so our allDogs is an array. To check the first element we can simply use an index of 0 and expect the text to be Jackie. And our tests passed.

Event handler testing

Let's have a test for an event handler such as onClick. I have created a Button.tsx component accepting text and onClick as props.

In a Button.test.tsx:

Rendering a component and getting an element are the same as in previous examples, however, to test handling events, let's import fireEvent from RTL. In an Arrange, we have to mock the onClick function by using jest.fn(), firstly because we have to pass some function as props to our Button and then we use it to assert our Act. In the Act part, we get our button and call click method on fireEvent passing our element as an argument. Then we can check if the button was clicked by the toHaveBeenCalledTimes assertion.

Testing stateful components

Frequently tested components have their state and testing them is fairly simple. For this case, I have created a plain component for changing the theme

In our DarkMode.test.tsx:

Testing our component involved handling click events so I used fireEvent from RTL. As you can see most of the time we test behavior in the response to state changes.

Testing simple custom hook

Let's tweak our DarkMode.tsx component and extract its logic to custom hook useDarkMode.tsx

In our useDarkMode.test.tsx:

Testing custom hooks at first glance seems quite abstract, but it is not! In order to understand it, we have to recall one of the rules of hooks in React - React hooks can only be called in React components or custom hooks. Our test is neither, so renderHook method from RTL comes in handy. We destructure result from that method and by calling .current on it we can access values our custom hooks returns. To call functions we use act method and in its callback, we pass our function.

As stated in docs it is redundant to test simple custom hooks when all their logic can be tested just by testing components using it, so it is recommended to test more complex or shared across multiple components custom hooks.

Docs: https://react-hooks-testing-library.com/

Rules of unit testing

  1. Write small and focused tests (focus on small units of code)

  2. Test behavior, not implementation

  3. Test for accessibility (rendering with different/wrong props, responding to interactions)

  4. Keep tests as simple and readable as possible

  5. Test edge cases (wrong inputs, error handling)

  6. Tests should be independent of each other

Unit vs Integration vs E2E tests

Unit tests are focused on testing individual components, hooks, redux in isolation. They are fast, reliable and easy to write.

Integration tests check if different components, functions, and modules work together as expected. They are useful for verifying communication between components and that application works as a whole.

E2E tests simulate real user interactions with the application, clicking buttons, scrolling, and entering text. These are the slowest and most complex but are a good way of checking how our app reacts to real interactions.


I am going to write soon part 2 of unit testing in React including async hooks, more advanced custom hooks, async custom hooks and redux components. Also, I am preparing to write an article about E2E testing in Cypress.