Quality Engineering
min read
July 3, 2023
July 3, 2023

Testing React Components with Jest and React Testing Library

Testing React Components with Jest and React Testing Library
Table of contents

Testing is an essential part of building software applications. It helps to identify bugs, errors, and unexpected behavior before deploying the code into production. React is a popular JavaScript library for building user interfaces, and Jest is a testing framework that makes it easy to test React components.

In this blog, we will consider how we can test React components using Jest and some of its best practices. 

Setting up Jest

To use Jest for testing React components, we need to install Jest with the below command:


npm install --save-dev jest

For additional information related to Jest setup, refer Getting Started with Jest

Starting with the unit tests

Before writing tests, let's look at the component we are testing. 

Code Description

In the below code, we have a Search Input and are fetching the blogs from an API call and showing them in the UI. The App.js file contains a label, Search Input field, and useFetch hook, which we will use to fetch the content of the blog posts. The code is below:



import React, { useState, useEffect } from 'react';
import useFetch from './useFetch';
import './App.css';
function App() {
    const [searchTerm, setSearchTerm] = useState('');
    const { data: fetchedPosts, error, isLoading } = useFetch('https://jsonplaceholder.typicode.com/posts');
    const filteredPosts = fetchedPosts?.filter((eachPost) => post.title.toLowerCase().includes(searchTerm.toLocaleLowerCase()));
    return (
        div className="container">
            <h1>Blog Posts</h1>
            <form>
                <label htmlFor="search">Search:</label>
                <input type="text" id="search" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} />
            </form>{' '}
            {isLoading && <p>Loading Posts...</p>} {error && <p>Error fetching posts: {error.message}</p>}{' '}
            {filteredPosts?.map((post) => (
                <li key={post.id} className="post-item">
                    <h2 className="post-title">{post.title}</h2>
                    <p className="post-body">{post.body}</p>
                </li>
            ))}
        </div>
    );
};

export default App;

We will be testing 3 things with this React component.

  • Testing React component in the loading state
  • Testing user interaction
  • Testing for failed requests

Now let's look at how to write unit tests for the above App.js component. 

Testing React component in a loading state

First, we will test for the loading state. We are mocking the return value from the 'useFetch' Hooks in the below code. Then we are rendering the App and expect to see the 'Loading Posts' text which will look like below.




    import React from 'react';
    import { render, screen, fireEvent } from '@testing-library/react';
    import '@testing-library/jest-dom/extend-expect';
    import useFetch from './useFetch';
    import App from './App';
    jest.mock('./useFetch');
    describe('App component', () => {
        it('renders loading message when fetching posts', () => {
            useFetch.mockReturnValue({ data: undefined, error: undefined, isLoading: true });
            render(<App />);
            expect(screen.getByText(/Loading posts/i)).toBeInTheDocument();
        });
    });


You can run the tests with the command ```npm test -- App.test.js``` and see the results if it passes. 


Test Results 

Testing React component in loading state

Testing user interaction 

To simulate user interaction, like clicking or typing, we will use fireEvent methods, and test the output. 


        import React from 'react';
        import { render, screen, fireEvent } from '@testing-library/react';
        import '@testing-library/jest-dom/extend-expect';
        import useFetch from './useFetch';
        import App from './App';
        jest.mock('./useFetch');
        describe('App component', () => {
            it('renders posts and filters them based on search term', () => {
                const posts = [
                    { id: 1, title: 'First Post', body: 'Body of first post' },
                    { id: 2, title: 'Second Post', body: 'Body of second post' },
                    { id: 3, title: 'Third Post', body: 'Body of third post' },
                ];
                useFetch.mockReturnValue({ data: posts, error: undefined, isLoading: false });
                render(<App />);
                expect(screen.getByText('Blog Posts')).toBeInTheDocument();
                const searchInput = screen.getByLabelText('Search:');
                fireEvent.change(searchInput, { target: { value: 'second' } });
                expect(screen.getByText('Second Post')).toBeInTheDocument();
                expect(screen.queryByText('First Post')).toBeNull();
                expect(screen.queryByText('Third Post')).toBeNull();
            });
        });

Here, we are testing the component by entering the text ‘second’ in the search field and validating that only the post related to it shows up.


Test Result 

Testing user interaction

Testing for failed requests

We are testing a scenario when the API fails to fetch the posts. It will throw some error messages.


        import React from 'react';
        import { render, screen, fireEvent } from '@testing-library/react';
        import '@testing-library/jest-dom/extend-expect';
        import useFetch from './useFetch';
        import App from './App';
        jest.mock('./useFetch');
        describe('App component', () => {
            it('renders error message when failing to fetch posts', () => {
                const error = new Error('Failed to fetch posts');
                useFetch.mockReturnValue({ data: undefined, error, isLoading: false });
                render(<App />);
                expect(screen.getByText(/error fetching posts:/i)).toBeInTheDocument();
                expect(screen.getByText(/failed to fetch/i)).toBeInTheDocument();
            });
        });

Test Results

Testing for failed request

Now the above-tested component is just one of the examples which we have shown in this blog. Let's consider multiple real-world scenarios that we can use, such as:

Login Page Testing

Let's say you have a login page for your application. You can use React Testing Library to simulate user input and test the login functionality. For example, you can use fireEvent.change to change the value of the email and password inputs and fireEvent.click to simulate the click of the login button. You can then use 'Expect' to verify that the correct action was taken, such as a redirect to the dashboard page or an error message if the login credentials were incorrect. 

Shopping Cart Testing

If you have a shopping cart feature on your website, you can use React Testing Library to test the functionality of adding and removing items from the cart. You can simulate user actions, such as clicking on the "Add to Cart" button or removing an item from the cart, and then checking that the cart has been updated correctly. For example, you can use getByText to find the cart total and check that it is updated correctly. 

Form Validation Testing

Another real-world scenario is testing form validation. You can use React Testing Library to simulate user input and verify that the form validation rules are working correctly. For example, you can simulate user input using fireEvent.change to change the value of a form input and then check that the correct error message is displayed if the input value is invalid. You can also verify that the form cannot be submitted when there are validation errors present. 

API Request Testing

If your application makes API requests, you can use React Testing Library to simulate user input and verify that the correct data is displayed in the UI based on the mocked API responses. 

These are just a few examples of applying Jest and React Testing Library to real-world scenarios. The possibilities are endless, and you can use these tools to test almost any aspect of your application. Let's look at some of the best practices. 

Best practices 

Use Snapshot Testing sparingly

Snapshot testing is a useful tool for detecting unexpected changes in your UI. However, relying too heavily on snapshot testing can lead to brittle tests that break easily. Instead, focus on testing the behavior of your components and use snapshot testing sparingly. 

Use Mocks and Spies

Mocks and spies can help you isolate your components from their dependencies, making your tests more reliable. Jest provides a powerful mocking system to create mocks and spies for components. 

Use Continuous Integration

Continuous integration (CI) means automatically building and testing your code whenever changes are made. CI can help catch errors early and ensure your tests always pass. In 2023, using a CI system is becoming increasingly common, and it's highly recommended to integrate it into your testing process. 

Test the behavior, not the implementation

Instead of testing specific functions or components, focus on testing the behavior and functionality of your app. It makes tests more reliable and less likely to break when you make changes. 

Keep tests simple and focused

Write small, focused tests that only test one thing at a time. It makes it easier to debug failures and maintain your tests over time. 

Use beforeEach() and afterEach()

Use these functions to set up and clean up test data. It can help prevent test data from interfering with other tests. 

Use waitFor() for asynchronous code

Use waitFor() to wait for asynchronous code to finish before continuing with your tests. It helps prevent false negatives and flaky tests. 

Use screen object to access the DOM

Use the screen object from React Testing Library to access the DOM elements rendered by your components. It provides a more reliable way to test your components than testing implementation details. 

Use fireEvent to simulate user actions

Use fireEvent to simulate user actions, such as clicking or typing. It makes it easy to test the behavior of your components in response to user interactions. 

Keep your tests up to date

As you make changes to your app, make sure to update your tests accordingly. It helps ensure that your tests remain accurate and reliable over time. 

Summing Up

Testing React components using Jest and React Testing Library are essential to building high-quality software applications. We have discussed how to set up Jest and write some tests to check the rendering, user interactions, failed API requests, and behavior of a React component. These tools can help us write comprehensive tests for React components and ensure they work as expected. 

Written by
Editor
No art workers.