Test React in 2026

A practical guide to testing React components, hooks, and user flows using Jest, React Testing Library, and Cypress to uncover real UI issues.

Guide Banner Image
Home Guide React Testing: How to test React components?

React Testing: How to test React components?

When I first started testing React, I assumed it was just about making sure each component rendered and updated correctly.

But the real problem showed up when everything passed in isolation yet broke the moment components interacted.

The contradiction surprised me: the more granular my tests were, the less they reflected how the app actually behaved. That gap between “tests passing” and “app working” is what pushed me to rethink how React testing should really be done.

Overview

Why Testing React Components is Important?

  • Ensures Stability: Detects bugs early and prevents regressions.
  • Improves User Experience: Ensures components behave as expected.
  • Boosts Maintainability: Simplifies debugging and refactoring.
  • Supports Confident Deployment: Reduces risks before production release.

Types of React Tests

  • Unit Tests: Test individual components in isolation (e.g., button clicks, state changes).
  • Integration Tests: Verify interactions between multiple components.
  • End-to-End (E2E) Tests: Simulate real user behavior across the app.
  • Snapshot Tests: Ensure UI consistency by comparing component renders.

In this article, I’ll break down how to test React the way it actually behaves, not the way we hope it behaves, best practices, and more.

Why testing React Components is important?

The basic idea behind implementing React testing on your application is to ensure it works as intended. However, the broader idea behind conducting tests on your React application is to make it less prone to errors and deliver a good user experience.

Furthermore, applications are typically updated frequently; testing ensures the application doesn’t break and the code is still reliable.

Are your React tests missing real issues?

Spot rendering bugs your local tests miss with BrowserStack Automate.

React Testing Library is a JavaScript testing utility that provides a set of utility functions that allow developers to interact with the components, such as clicking buttons, entering text, and checking for the presence of certain elements.

The library encourages developers to write tests that are more focused on user behavior and less focused on implementation details.

Types of React Tests

Whenever you’re set to perform React testing, it becomes difficult to deal with the prospect and often left puzzled, but it doesn’t have to be when you have the right tests in your kit. Knowing the different types of tests unlocks the power of testing and helps you deliver better code to ensure your application is reliable, functional, and efficient.

Three types of tests can be written for React applications:

  1. Unit tests: These types of tests are performed to test the functionality of an individual component in isolation.
  2. Integration tests: These tests ensure the proper functioning of different components working together. It tests the interaction between the parent and child components or when the components contain some dependencies.
  3. E2E tests: These tests ensure a proper user experience is received from the user perspective by testing an entire application.

Are your React tests missing real issues?

Spot rendering bugs your local tests miss with BrowserStack Automate.

Writing Unit Tests for React Components

Unit tests are performed to test an individual component in isolation. The idea behind implementing unit tests for React components is to ensure each component works as intended regarding its design specifications.

The anatomy of unit tests follows the AAA approach, which stands for:

  • Arrange: In this section, as the name suggests, prepare the system for testing. It involves configuring the necessary dependencies and creating required objects.
  • Act: In this section, you operate on the system that is under test. It may involve calling a function.
  • Assert: In this part, ensure the outcome matches the expected result. It involves making assertions on the system’s behavior that is under test to ensure its outcome.

Are your React tests missing real issues?

Spot rendering bugs your local tests miss with BrowserStack Automate.

Test React Components using Jest

Jest is a popular testing framework for React that is simple and powerful enough to write and execute unit tests in React. Jest offers a variety of features for testing React components, such as mocking, snapshot testing, and more.

Prerequisites

To run React tests using Jest, you only need a small set of essential packages. Here’s exactly what must be installed:

Core installations:

1. Jest

The test runner and assertion library.

 npm install --save-dev jest

2. Babel Jest + Babel presets

Required so Jest can understand JSX and modern JavaScript.

 npm install --save-dev babel-jest @babel/preset-env @babel/preset-react

3. React Testing Library (recommended for component tests)

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

If using Create React App:

Nothing extra is needed — Jest is already preconfigured.

You only install React Testing Library:

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

Optional but common utilities:

user-event (simulate real user interactions)

 npm install --save-dev @testing-library/user-event

ts-jest (only for TypeScript projects)

 npm install --save-dev ts-jest @types/jest

Now, perform testing of React Components with the help of Jest. In this example, you shall test the ‘HelloWorld’ component which contains the text ‘helloworld’.

Step 1: Install Jest

npm install --save-dev jest

Step 2: Write a Test

Create a .test.js file and paste the following test script inside the file. Write the test with the ‘it’ or ‘test’ function offered by the Jest library.

import React from 'react';
import { render } from '@testing-library/react';
import HelloWorld from './HelloWorld';

test('renders a message', () => {
const { getByText } = render(<MyComponent message="Hello, World!" />);
const messageElement = getByText(/Hello, World!/i);
expect(messageElement).toBeInTheDocument();
});

Step 3: Execute the Test

To execute the test, run the following command in the terminal. This test case tested if the application contains the ‘helloworld’ text.

npx jest HelloWorld.test.js

Execute Jest Test

Snapshot Testing

Snapshot testing is generally performed for the testing purposes of the user interface. While running the Snapshot test, Jest creates a series of components compared with the previously saved snapshot. If the snapshot matches the previous one, the test passes otherwise, the test fails.

First Install the latest version of React test renderer with the following command:

npm install react-test-renderer

Write a Snapshot test for the previous test.

import React from 'react';
import renderer from 'react-test-renderer';
import HelloWorld from './HelloWorld';

test('renders correctly', () => {
const component = renderer.create(<HelloWorld />);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});

Mocking Dependencies

Best Practices for Writing Unit Tests

Here are some of the Unit Testing best practices that must follow to ensure higher reliability, functionality, and efficiency of your unit tests.

  • Unit tests are conducted solely to test the individual component without the interference of any other component. Therefore, each component that you put under unit tests must be independent and not rely on any other component or have any dependency.
  • To keep the unit test focussed, you must test one thing at a time and should test a specific behavior of a component.
  • You must invest some time in writing the test names to identify and fix issues if the test fails. You should use descriptive names that specify what you’re testing in a component and what should be the expected outcome.
  • Make use of CI/CD pipelines to automate your tests, ensuring the software is always working.

BrowserStack Automate lets you run React unit tests alongside real-browser checks, helping you confirm that component logic and UI behavior hold up across actual environments—not just in local setups.

Talk to an Expert

Writing Integration Tests for React Components

In integration testing, the aim is to verify that different system components can work together correctly. To put it another way, an integration test is performed to evaluate how well various components interact.

The anatomy of an integration test involves four parts, setup, test, assertion, and teardown.

  1. Setup: In this part, set up the environment for the system that is to be tested. This may involve configuring dependencies, components, and load data to ensure they are ready for smooth testing.
  2. Test: This is the part where the integration test is performed. This may include calling out multiple functions in a test file to verify the behavior of components working in combination.
  3. Assertion: Ensure the outcome matches the expected result in this part. It involves making assertions on the system’s behavior under different conditions to ensure the expected outcome.
  4. Teardown: In this part, eliminate the resources used solely for testing purposes. It may involve deleting temporary data or shutting down the unnecessary components required during the testing.

Are your React tests missing real issues?

Spot rendering bugs your local tests miss with BrowserStack Automate.

React Testing with Enzyme

Enzyme is a JavaScript testing library for React that offers a variety of tools for testing React components. Install Enzyme and understand how to perform integration testing with the help of Enzyme.

Step 1: Install Enzyme and Enzyme adapter

npm install --save-dev @cfaester/enzyme-adapter-react-18

Step 2: Import Enzyme and configure Enzyme adapter

import Enzyme from 'enzyme';
import Adapter from '@cfaester/enzyme-adapter-react-18';

Enzyme.configure({ adapter: new Adapter() });

Shallow Rendering

Shallow rendering is a process of performing React testing in complete isolation without interfering with any of its child components. It is quicker than full rendering, which renders the entire component tree.

In this example, we’re using Enzyme’s shallow() function to render the HelloWorld component and then using Enzyme’s find() function to locate the <p> element within the component and check its text content.

Create a test file and continue with the following script.

import React from 'react'; 
import Enzyme, { shallow } from 'enzyme'; 
import Adapter from '@wojtekmaj/enzyme-adapter-react-18'; 
import HelloWorld from './HelloWorld';

Enzyme.configure({ adapter: new Adapter() });

describe('HelloWorld', () => { 
it('renders correctly', () => { 
const wrapper = shallow(<MyComponent />); 
expect(wrapper.find('p').text()).toEqual('Hello, world!'); 
}); 
});

Mounting and Full Rendering

Mounting and Full Rendering is a process of rendering an entire component in a test, including its child components into the DOM. In Full Rendering, the actual rendering of child components occurs whereas, in Mounting only initialization happens.

Consider a similar practical example to understand Mounting and Full Rendering while testing React components.

import React from 'react';
import Enzyme, { mount, render } from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-18';
import HelloWorld from './HelloWorld';

Enzyme.configure({ adapter: new Adapter() });

describe(‘HelloWorld', () => {
it('mounts correctly', () => {
const wrapper = mount(<HelloWorld />);
// test component behavior
expect(wrapper.find('button').text()).toEqual('Click me');
});

it('renders correctly', () => {
const wrapper = render(<HelloWorld />);
// test component output
expect(wrapper.find('button').text()).toEqual('Click me');
});
});

Snapshot Testing

Here is an example of performing Snapshot testing with the help of Enzyme.

import React from 'react';
import { shallow } from 'enzyme';
import HelloWorld from './HelloWorld';

describe('HelloWorld', () => {
it('should render correctly', () => {
const wrapper = shallow(<MyComponent />);
expect(wrapper).toMatchSnapshot();
});
});

Mocking Strategies in React

Mocking in React tests helps isolate component behavior by replacing external dependencies with controlled, predictable versions. It ensures tests stay fast, stable, and focused on what the component itself is responsible for.

  • When to mock: Mock only what sits outside the component: network requests, utility modules, analytics calls, custom hooks imported from other files, and browser APIs. This keeps tests deterministic and avoids flaky behavior caused by real external interactions.
  • When to avoid mocking: Avoid mocking React itself, DOM behavior, or other components your test genuinely depends on. Over-mocking hides integration issues and leads to tests that pass even when the UI is broken.
  • Mocking API calls: Use Jest to mock fetch, Axios, or API modules so components can be tested against success, error, and edge-case responses without calling real servers.
  • Mocking child components: For components with heavy children (charts, maps, complex UI widgets), replace the child with a simple mock to focus the test on parent behavior rather than rendering cost or irrelevant interactions.
  • Mocking custom hooks: If a component imports a custom hook—such as an auth or data hook—mock the module and control the hook’s return value. This is useful for simulating logged-in/logged-out states or async data loading.
  • Mocking context values: Instead of mocking context modules, wrap components in lightweight test providers. This mirrors real usage and avoids implementation-based tests.
  • Mocking browser APIs and timers: Use Jest spies or fake timers when components rely on localStorage, matchMedia, setTimeout, or setInterval. This makes time-based or environment-based behavior testable.
  • Core principle: Mock what your component doesn’t own, keep real what it does. This preserves meaningful test coverage and avoids fragile, misleading test suites.

Mocking dependencies is the procedure followed to ensure the system under test is isolated from external dependencies or interaction with other modules by replacing the module’s implementation or a function with the mock implementation we define.

Consider a practical example of how to mock dependencies in Jest.

const fetchUser = async (userId) => { 
const response = await fetch(`https://api.example.com/users/${userId}`); const data = await response.json(); 
return data; };

const fetch = require('node-fetch');

jest.mock('node-fetch');

describe('fetchUser', () => {

it('returns the user data', async () => {

const mockResponse = { name: 'example' };

fetch.mockResolvedValueOnce({

json: jest.fn().mockResolvedValueOnce(mockResponse),

});

const result = await fetchUser(123);

expect(fetch).toHaveBeenCalledTimes(1);

expect(fetch).toHaveBeenCalledWith('https://api.example.com/users/example');

expect(result).toEqual(mockResponse); 
});

});
  • In this example, jest.mock() is used to mock the node-fetch module.
  • Then, in the test, fetch.mockResolvedValueOnce() is used to create a mock answer for the fetch() call inside of fetchUser().

Best Practices for Writing Integration Tests

Here are some of the best practices that must follow to ensure higher reliability, functionality, and efficiency of your integration tests.

  • Integration tests can be very time-consuming, therefore, it is a good practice to automate the testing process to save time and prevent any slight human error.
  • You must fabricate proper documentation of your Integration tests to ensure they are repeatable and easy to grasp. Documentation is also a necessary aspect as it helps gradually upgrade the overall quality of tests.
  • The entire idea behind implementing Integration tests is to ensure the proper functioning of interaction among different components. Therefore, you must test the system as a whole.
  • Try to incorporate data that reflects upon real-world scenarios for finding out the product’s best and worst use cases.

Writing End-to-End Tests for React Components

End-to-end testing, often known as E2E testing, involves testing an application’s complete lifecycle, including all of its levels and components. In E2E testing, the software is expected to work as intended while being put under real-world scenarios.

The anatomy of E2E testing for React components includes:

  • Identifying test scenarios: In this step, test scenarios are identified based on the application’s requirements.
  • Set up the test environment: The testing environment should be similar to the production environment, to replicate the real-world scenarios as closely as possible. It involves having the same configurations on the testing and production end.
  • Write test scripts: Various testing frameworks are available to run automated test scripts simulating real user interactions with the application.
  • Execute the test scripts: Automated test scripts must be executed in a suitable testing environment to ensure higher reliability of tests.
  • Analyze the test results: This part involves generating reports on how to test went to identify various issues and defects in the application.

React Testing with Cypress

Cypress is an end-to-end testing framework based on Node.Js that supports JavaScript/TypeScript as the programming language. Cypress is popular for its ease while performing web application testing.

Now, consider the practical implementation of Cypress while testing React components.

Step 1: Installing Cypress

You can use any IDE of your choice for performing testing using Cypress. This example, uses Visual Studio IDE.

To install Cypress, follow the command in the terminal.

npm install cypress --save-dev

After installing Cypress, open the package.json file and paste the following script inside the script tag.

package.json file

Now run the command to open the Cypress.

npx run test

Step 2: Create a test file

To create a test file, create a new file with the extension .spec.js in the project directory, cypress/integration.

Making Assertions and Interacting with React Components

For example, visit a demo website and click a button that adds the item to a shopping cart. Let’s write a test that verifies that the item is added to the cart after the button is clicked.

Here is how you may carry out it using Cypress assertions:

// Navigate to the page with the shopping cart button
cy.visit('https://bstackdemo.com/')

// Click the shopping cart button
cy.get('#2').click()

// Assert that the item was added to the cart
cy.get('#cart-items')
.should('contain', 'iPhone12')
.should('contain', '$799')
.should('contain', '1')

Tradeoffs When Testing React

Testing React applications involves balancing speed, accuracy, environment realism, and maintenance effort. Each testing layer and tool solves different problems, but each also introduces tradeoffs teams must account for.

1. Balancing Unit, Integration, and E2E Tests

Each layer in the testing pyramid offers different strengths and limitations.

  • Unit tests: Fast, inexpensive, and ideal for pure logic and isolated components. They provide quick feedback but miss rendering issues, browser inconsistencies, and integration bugs.
  • Integration tests: Validate interactions across components and shared state. They catch problems that unit tests overlook, but they are slower and require more careful setup.
  • End-to-end (E2E) tests: Most realistic because they run user workflows in an actual browser. They detect routing, API, network, and environment issues. However, they are slower, more brittle, and more expensive to run at scale, so they’re best reserved for critical flows.

Tradeoff summary: heavy investment in unit tests, a focused layer of integration tests, and a small but essential set of E2E tests for high-value paths.

2. Choosing the Right Tools

Different testing tools excel at different layers.

  • React Testing Library + Jest: Encourages testing from the user’s perspective, making tests more resilient. Excellent for unit and lightweight integration testing, but not suited for validating CSS, layout, or browser rendering.
  • Cypress / Playwright: Strong choices for component testing and full-flow E2E testing. They run real browser APIs and provide stable automation but require heavier CI resources.
  • Legacy tools like Enzyme: Offer deep access to internal component structure but do not align with modern React testing practices and are declining in community support.

Tradeoff summary: use RTL + Jest for unit/integration; Cypress/Playwright for E2E and complex UI behavior.

Are your React tests missing real issues?

Spot rendering bugs your local tests miss with BrowserStack Automate.

3. Local Testing vs Cloud & Real Device Testing

Where BrowserStack meaningfully shifts the equation.

  • Local & emulator-based testing: Fast and simple to run, ideal for development loops. But local testing is limited to your machine’s browsers and cannot reveal device-specific rendering issues, OS differences, touch interactions, or real-world network behavior.
  • Real device and cross-browser testing:  Provides access to real mobile devices, full browser versions, diverse OS configurations, and real network conditions. This uncovers layout issues, compatibility gaps, and timing problems that never surface in emulators. The tradeoffs have slightly higher latency, and integrates cloud testing into CI pipelines.

Tradeoff summary: Choose real device cloud platform like BrowserStack for cross-browser validation, mobile UI testing, visual regression checks, and final acceptance tests before release.

4. Flakiness, CI Cost, and Execution Speed

Real browser and device tests offer higher confidence but introduce slower execution times and potential flakiness due to network, timing, or environmental differences.
Parallelization, test batching, and selective testing strategies reduce CI time—capabilities that BrowserStack supports at scale—though they also increase resource usage.

5. Debugging and Test Maintainability

  • Unit/integration tests provide quick, focused error messages and are easier to debug.
  • E2E failures are harder to triage because issues may stem from timing, DOM changes, network events, or device nuances.

Real-device platforms like BrowserStack help offset this by providing screenshots, logs, videos, and device-level insights that reduce debugging time.

6. Security, Data, and Compliance Considerations

When tests use live or sensitive data, external cloud environments require careful handling. Sanitized test accounts, masked data, and access controls become essential. Enterprises may need private device clouds or data residency controls depending on compliance needs.

Why choose BrowserStack for React Testing?

BrowserStack Automate allows React teams to run their UI and end-to-end tests on real browsers instead of relying on a single local setup. Once your React tests run locally—whether built with Cypress, Playwright, Selenium, or WebdriverIO—you can point them to BrowserStack’s cloud by adding your Automate credentials and specifying the browser/OS you want through capabilities. This lets you validate critical React flows such as routing, forms, dynamic components, modals, loading states, and error handling under real conditions.

For apps still in development, BrowserStack Local makes it possible to test localhost or private staging URLs on cloud browsers. Parallel execution cuts down test time by running checks across Chrome, Firefox, Edge, and Safari simultaneously, while detailed logs, videos, and network traces make debugging cross-browser issues much easier. With CI/CD integration, these tests can run automatically on every commit or pull request, ensuring your React UI behaves consistently before each release.

Try BrowserStack Automate

Conclusion

When you’re testing a React app, the real value shows up when the UI behaves the same way in your tests as it does for your users. Unit, integration, and end-to-end tests with tools like Jest, Enzyme, and Cypress help you catch problems early, but they only go so far if everything runs on one machine.

If you want to see how your app actually holds up, try running those same tests on real browsers and devices. BrowserStack’s Real Device Cloud gives you that view. It lets you check layout, interactions, and browser quirks in the same environments your users rely on. It’s a simple way to confirm your React app is truly ready before you put it in their hands.

Tags
Automated UI Testing Automation Testing

FAQs

Testing in React ensures that components behave as intended when users interact with them. It helps catch issues in rendering, state updates, routing, and UI logic before they reach production.

React developers often use Jest for unit tests, React Testing Library for component behavior, and Cypress or Playwright for end-to-end testing. Each tool covers a different layer of the testing workflow.

Yes, for most component tests. Mocking API calls, utilities, and external dependencies keeps tests predictable and avoids relying on live servers. It also helps simulate success, failure, and edge cases.

Are your React tests reliable?
Run React tests with BrowserStack to catch layout issues local tests miss.

Get answers on our Discord Community

Join our Discord community to connect with others! Get your questions answered and stay informed.

Join Discord Community
Discord