Integration Testing In React
Published: 2024-03-11 17:34:26

by [Mba Jude](https://blog.openreplay.com/authors/mba-jude)


<blockquote><em>
Testing in web development is a highly overlooked but crucial aspect of the web development process. It ensures an application's reliability and functionality by detecting and removing bugs and unexpected behavior. It also increases user satisfaction and confidence in the application. This article will teach you all about integration testing so you can ensure the quality of your app.
</em></blockquote>


<div style="background-color:#efefef; border-radius:8px; padding:10px; display:block;">
<hr/>
<h3><em>Session Replay for Developers</em></h3> 
<p><em>Uncover frustrations, understand bugs and fix slowdowns like never before with <strong><a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a></strong> — an open-source session replay suite for developers. It can be <strong>self-hosted</strong> in minutes, giving you complete control over your customer data.</em></p>
<img alt="OpenReplay" style="margin-top:5px; margin-bottom:5px;" width="768" height="400" src="https://raw.githubusercontent.com/openreplay/openreplay/main/static/openreplay-git-hero.svg" class="astro-UXNKDZ4E" loading="lazy" decoding="async">
<p><em>Happy debugging! <a href="https://openreplay.com" target="_blank">Try using OpenReplay today.</a></em><p>
<hr/>
</div>


Integration testing in [React](https://react.dev/) refers to testing the interactions between the different components in a React application, i.e., how these components work together to create a specific functionality or workflow. Components are the foundation of the user interface in React applications, and they frequently interact with one another to create different workflows. Integration tests ensure that these components function properly when combined to form the desired functionality.
In this guide, we will delve deep into the complexities of integration testing, giving you the skills to build React applications confidently and dependably in every component's interaction.

## Writing Your First Integration Test
To perform integration testing in React, you'll need the following two major tools:

- React Testing Library (RTL)
- [Jest](https://jestjs.io/)

RTL is a React library designed for testing React components. Jest is a [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript) testing framework used for testing JavaScript codebases. Let's create and configure a new React project called "testing" using `create-react-app`.

```
npx create-react-app testing
```
When you create a new React project using `create-react-app`, RTL and some of its dependencies are automatically installed, as shown below: 

![RTL_dependencies](https://blog.openreplay.com/images/integration-testing-in-react/images/image1.png)

If that is not the case, you can simply install them manually using the following commands:

```
npm install --save-dev @testing-library/react
npm install --save-dev @testing-library/jest-dom
npm install --save-dev @testing-library/user-event
```

Now that we're all set up, let's start testing!

We'll create two components that interact with each other: a Parent component called `MainBody.js`, which renders a Child component called `ChangeMainBody.js`.

```javascript
//ParentComponent called MainBody.js
import React, { useState } from "react";
import ChangeMainBody from "./ChangeMainBody";

function MainBody() {
  const [mainBody, setMainBody] = useState("Initial Text");

  return (
    <div>
      <h1>{mainBody}</h1>
      <ChangeMainBody setMainBody={setMainBody} />
    </div>
  );
}

export default MainBody;
```

```javascript
//ChildComponent called ChangeMainBody.js
import React, { useState } from "react";

function ChangeMainBody({ setMainBody }) {
  const [ChangeMainBody, setChangeMainBody] = useState("Changed Text");

  function handleClick() {
    setChangeMainBody("New Text");
    setMainBody("New Text");
  }

  return (
    <div>
      <div>{ChangeMainBody}</div>
      <button onClick={handleClick}>Change Main Body</button>
    </div>
  );
}

export default ChangeMainBody;
```

The two components above interact with each other via props such that when the button in the `ChangeMainBody` component is clicked, the `h1` text in the `MainBody` component is updated to "New Text" using the `setMainBody` function passed as a prop to the `ChangeMainBody` component.

![bandicam 2024-02-10 15-02-47-038](https://blog.openreplay.com/images/integration-testing-in-react/images/image2.gif)


Let's write an integration test for this interaction using Jest and React Testing Library. To accomplish this, we will test the Parent Component(`MainBody.js` in this example), which houses the Child Component. Create a file named `MainBody.test.js` in your test folder and type in the following code to implement your first integration test.

```javascript
//MainBody.test.js
import { render, screen, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom";
import MainBody from "../MainBody";

test("Button Click should change MainBody h1 text", () => {
  render(<MainBody />);
  const buttonElement = screen.getByText(/change main body/i);
  fireEvent.click(buttonElement);
  const headingElement = screen.getByRole("heading", { name: /new text/i }); //Check to see that h1 has been updated after click
  expect(headingElement).toBeInTheDocument();
});
```

Let's break down the code sample above:

In this case, we use the `render` method to mount the component for testing, which is the `MainBody` component. We then get the button from the `ChangeMainBody` component using the `screen.getByText` method. We simulate a user clicking this button using `fireEvent.click` and then use the `expect` method to check that the `h1` in the `MainBody` component has been updated following the button click.

After completing this [test suite](https://en.wikipedia.org/wiki/Test_suite), type the following command in your terminal:
```
npm run test
```
Everything being equal, you should get the following output indicating the test has passed: 

![Integration_test1](https://blog.openreplay.com/images/integration-testing-in-react/images/image3.png)

Congratulations!! You have completed your first integration test! You realize that you could simulate the interactions between the `MainBody` and `ChangeMainBody` components to test the `h1` text update functionality without having to manually perform the actions yourself. This is the core of integration testing in React.

<CTA_Middle_Frameworks />



##    Mocking Dependencies

Mocking is often an essential aspect of testing in React. It is defined as the practice of substituting parts of your application with simulated or controlled versions during the testing process. 

Integration testing involves checking if the different components of your application work well together. Some of these components often rely on external resources, such as functions, services, or APIs, to function properly. These external resources are referred to as **dependencies**. Mocking enables you to simulate the behaviors of these dependencies, allowing you to test solely your component interactions without making actual calls to or relying on these dependencies.

Now you might wonder, "Isn't it a better idea to test our components without mocking these dependencies? We'll be testing both our frontend and backend functionalities". However, the following points will explain why this is a very bad idea and highlight the importance of mocking in your tests.

* Some external dependencies, such as external API services, begin charging per usage after a certain number of requests. If you are developing a large React application, you will be running tests repeatedly, and if you have components that make API requests to these charged services, you'll make these requests every single time a test is run. Eventually, you will start being charged for each request, which is something you want to avoid. With mocks, you'll be able to simulate the behavior and response of these external API services without making actual calls to them and as such, avoid charges for requests.
* Real API calls may require a network request, which can be time-consuming and result in slow test execution. This means that if you have a React application with many tests, you may have to wait for extended periods for all of them to run. This may discourage you from running your tests as frequently as you should. Mocking helps speed up these test executions by providing immediate responses, allowing you to run your tests more frequently and efficiently.
* Testing components that require external services without mocking implies you're relying on the availability and correctness of those services during testing. If, for some reason, the external service is unavailable or returns unexpected data, your tests will fail. Whereas with mocks, you can isolate the components being tested from their dependencies by using mock versions of the services, allowing you to focus on testing only the components' behavior and interactions with other components without worrying about the service's availability or correctness.

Now that you understand the need for mocks in your tests let's start mocking our dependencies. In this guide, we will mock all of our dependencies using [Jest Mocks](https://jestjs.io/docs/mock-functions).

### Mocking React Components

We'll start by mocking React components. Suppose you have a component, `AboutComponent` that renders some text and an image.

```javascript
import React from "react";

function AboutComponent() {
  return (
    <div>
      <h1>About</h1>
      <p>This is the about page</p>
      <img src="/image_path" alt="about_image"></img>
    </div>
  );
}

export default AboutComponent;
```

Fetching and displaying images will typically slow down your tests. You should mock this component to only test that it renders correctly. To mock a React component with Jest mocks, you use the `jest.mock` function to mock the **path** to the module that exports the component and return a mocked component with its custom implementation.

In your test file, type the following code:

```javascript
import {render} from '@testing-library/react';
import '@testing-library/jest-dom';
import AboutComponent from './AboutComponent'  //Specify your own path

//Mock path to module
jest.mock("./AboutComponent", () => {
  return () => {
    return (
      <div>
        <mock-image data-testid="about-image" alt="mock_image" />
      </div>
    );
  };
});

test("Mocked About page is visible", async () => {
  render(<AboutComponent />);

  const fullImage = screen.getByTestId(/about-image/i);
  expect(fullImage).toBeInTheDocument();

  const aboutPage = await screen.findByAltText(/mocked_image/i);
  expect(aboutPage).toBeInTheDocument();
});

```

Let's understand the code above.

We import the component we want to mock, `AboutComponent` into our test file. We mock this component by calling the `jest.mock` function. We pass as arguments to this function the path to the module containing the component and a factory function that returns the mocked component along with the mocked image.

In our test suite, we render the `AboutComponent` for testing. We then use the `screen.findByTestId` to find an element with a `data-testid` attribute of "about-image" and assert that it exists in the document. We also use the `screen.findByAltText` to find the `alt` text of this element and assert that its value is "mocked-image". 

When you run the test, Jest replaces the original `AboutComponent` with the mocked component returned by the `jest.mock` factory function. This causes the test to pass, as seen below:

![Component_mock](https://blog.openreplay.com/images/integration-testing-in-react/images/image4.png)



### Mocking External Modules and APIs

API interactions are a common feature of React applications. In this guide, we will mock API requests made with the [Axios](https://axios-http.com/docs/intro) library. We will also use Jest for the API mocking. Let's look at a simple example.

Suppose you have a file, `apitest.js` in which you make an `axios GET` request to an external server. This file contains a function `fetchTodos`, which handles the request like so:

```javascript
import axios from "axios";

const fetchTodos = async () => {
  const res = await axios.get("https://jsonplaceholder.typicode.com/todos/1");
  if (res && res.data) {
    console.log(res.data);
    return res.data.title;
  }
};

export default fetchTodos;
```

In your main component, you import the `fetchTodos` function and use it like so:

```javascript
//Main Component
import React, { useEffect } from "react";
import fetchTodos from "./apitest";

function DataComponent() {
  useEffect(() => {
    fetchTodos();
  }, []);
}

export default DataComponent;
```

To mock this request, you must mock `axios`, the external module that handles the request. In your test file, type the following code:

```javascript
//DataComponent.test.js
import "@testing-library/jest-dom";
import fetchTodos from "../apitest";
import axios from "axios";

jest.mock("axios", () => {
  return {
    get: jest.fn().mockResolvedValue({ data: { title: "mocked data" } }),
  };
});

test("Testing with mocked axios", async () => {
  const result = await fetchTodos();
  expect(result).toEqual("mocked data");
});

```

From the code above, we import the `fetchTodos` function that handles the request, and`axios`. We then mock `axios` using the `jest.mock` function by specifying its path and the factory function as arguments to the `jest.mock` function. The factory function returns an object containing a mocked implementation of the actual API call in the `fetchTodos`. In this mocked implementation, we replace the `get` method with a Jest mock function, `jest.fn()`, and chain with `mockResolvedValue` to return a resolved value called `mocked-data`. This means whenever `axios.get` is called, it returns the value "mocked-data".

In the test, we call the `fetchTodos` function, which uses `axios`, and assert that the returned result matches the expected value "mocked-data". When you run the test, Jest will replace the actual API request in the `fetchTodos` function with the mocked request in the Jest mock function, causing the test to pass.

![API_Pass](https://blog.openreplay.com/images/integration-testing-in-react/images/image5.png)

This test allows you to test the functionality of the `fetchTodos` function without relying on the actual API call for data retrieval.



##    Testing Navigation and Route Changes
Routing is a fundamental aspect of React applications, so it is critical to ensure that navigation behavior and route mechanisms work as expected. Using React Testing Library, you can simulate route changes and test navigation behavior.

We will create 2 Route components: `DataComponent` and `AboutComponent`. The `DataComponent` will contain a link that, when clicked, navigates you to the `AboutComponent`.

```javascript
//DataComponent.js
import React from "react";
import { Link } from "react-router-dom";

function DataComponent() {
  render(
    <div>
      <Link to="/about">About</Link>
    </div>,
  );
}

export default DataComponent;
```

```javascript
//AboutComponent.js
import React from "react";

function AboutComponent() {
  return (
    <div>
      <h1>About</h1>
      <p>This is the about page</p>
    </div>
  );
}

export default AboutComponent;
```

Resulting output:

![bandicam 2024-02-10 13-46-24-207](https://blog.openreplay.com/images/integration-testing-in-react/images/image6.gif)


To test this navigation functionality, create a test file named `DataComponent.test.js` in your test folder and type in the following code:

```javascript
//DataComponent.test.js
import { fireEvent, render, screen } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";
import DataComponent from "../DataComponent";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import AboutComponent from "../AboutComponent";

//Navigation Test
test("Navigation to About page is possible", async () => {
  render(
    <Router>
      <Routes>
        <Route path="/" element={<DataComponent />} />
        <Route path="/about" element={<AboutComponent />} />
      </Routes>
    </Router>,
  );
  const aboutLink = screen.getByRole("link", { name: /about/i });
  fireEvent.click(aboutLink);
  const aboutPage = await screen.findByText(/This is the about page/i);
  expect(aboutPage).toBeInTheDocument();
});
```

Let's break down the test:

We begin by rendering the components for testing via their respective routes and wrapping them with the `Router` component. This sets up [React Router](https://reactrouter.com/en/main) for testing by simulating the presence of the `BrowserRouter` in a real application. The home ('/') route displays the `DataComponent` which contains the `About` link. We get this link using `screen.getByRole` and use the `fireEvent.click` to simulate a user clicking the link. After the simulated click, we assert that the route has changed by checking to see if the text "**This is the about page**" now appears on the screen, as this text is only found in the `AboutComponent`. 

Running this test, you should get the following output indicating that it passes.

![Routing_test](https://blog.openreplay.com/images/integration-testing-in-react/images/image7.png)


## Conclusion

To conclude, integration testing in React is a fundamental practice that enables developing robust, reliable, and maintainable applications by ensuring that components work harmoniously together. Knowing that components work together as intended boosts the confidence of developers and teams in the overall behavior of the application. By validating that the application behaves correctly when different parts are integrated, developers can deliver a more reliable, high-quality product to end-users.