Key Takeaways
- Unit testing React components can be streamlined with Jest and React Testing Library.
- The React Testing Library focuses on testing user interactions with the DOM rather than component internals.
- Using Jest allows for structured test writing and reusable setup with functions like
beforeEach. - React components can be tested in isolation by mocking external dependencies like HTTP requests.
Basic Example
ToggleText.js
import { useState } from "react";
const ToggleText = () => {
const [showText, setShowText] = useState(true);
return (
<>
{showText && <p data-testid="text">Hello world</p>}
<a data-testid="toggle" onClick={() => setShowText(!showText)}>Toggle</a>
</>
);
};
export default ToggleText;
ToggleText.test.js
import { fireEvent, render, screen } from "@testing-library/react";
import ToggleText from "./ToggleText";
test("toggles text", () => {
render(<ToggleText />);
const text = screen.getByTestId("text");
const toggle = screen.getByTestId("toggle");
expect(text).toBeInTheDocument();
fireEvent.click(toggle);
expect(text).not.toBeInTheDocument();
});
To run the test, execute:
npm test
Unit Testing with Jest and React Testing Library
The example above utilizes Jest, a widely-used JavaScript testing framework. If you're using Create React App (CRA), you already have Jest and the React Testing Library set up out of the box.
Create React App includes the following testing dependencies:
{
...
"dependencies": {
"@testing-library/jest-dom": "^latest",
"@testing-library/react": "^latest",
"@testing-library/user-event": "^latest",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "latest",
"web-vitals": "^latest"
},
...
}
These libraries allow for DOM manipulation and assertions that DOM nodes reflect expected states.
What is the React Testing Library?
React Testing Library is part of the broader Testing Library family, focused on allowing developers to write tests that reflect how users interact with the application. It provides utilities needed to interact with and assert against DOM elements rather than internal component states.
Jest vs React Testing Library
Understanding Jest
Jest is not bound to React or any specific library. It's a robust framework for testing JavaScript that runs tests, sets up test execution environments, and facilitates the writing of test code with features like mocks and spies.
describe('sum', () => {
it('correctly sums two values', () => {
expect(sum(2, 3)).toBe(5);
});
});
Jest is designed to handle everything from unit tests to integration tests, supporting a wide range of JavaScript applications.
Understanding React Testing Library
The React Testing Library is a lightweight solution focused on testing the rendered output of components rather than their internals. It's particularly good at testing user interactions, mapping these interactions to real DOM elements.
import { fireEvent, render, screen } from "@testing-library/react";
import ToggleText from "./ToggleText";
test("toggles text", () => {
render(<ToggleText />);
const text = screen.getByTestId("text");
const toggle = screen.getByTestId("toggle");
expect(text).toBeInTheDocument();
fireEvent.click(toggle);
expect(text).not.toBeInTheDocument();
});
React Unit Testing Examples
Mocking HTTP Requests
Sometimes, your component may rely on asynchronous data fetching. Testing these requires mocking the HTTP requests to isolate component behavior.
ToggleText.js
import { useState } from "react";
import axios from 'axios';
const ToggleText = () => {
const [showText, setShowText] = useState(false);
const getData = async () =>
axios
.get("https://dummyjson.com/products/1")
.then(response => {
const { data } = response;
const { id } = data;
if (id) {
setShowText(true);
}
})
.catch(() => {
setShowText(false);
});
return (
<>
{showText && <p data-testid="text">Hello world</p>}
<a data-testid="toggle" onClick={getData}>Toggle</a>
</>
);
};
export default ToggleText;
ToggleText.test.js
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import ToggleText from "./ToggleText";
import axios from 'axios';
jest.mock('axios');
test("toggles text after fetching data", async () => {
axios.get.mockResolvedValue({
data: { id: 1 }
});
render(<ToggleText />);
const toggle = screen.getByTestId("toggle");
fireEvent.click(toggle);
await waitFor(() => {
expect(screen.getByTestId("text")).toBeInTheDocument();
});
});
This example shows how to mock axios with Jest to provide controlled responses to HTTP requests within a test environment.
Best Practices for React Unit Testing
Writing maintainable unit tests involves structuring them for readability and reusability. Following best practices allows you to effectively test React components.
Using describe Properly
Utilize describe blocks to group related tests and features, keeping tests organized and readable.
describe('ToggleText', () => {
it('should toggle text', () => {
// Test implementation
});
describe('when button is clicked', () => {
it('should trigger HTTP request', () => {
// Test implementation
});
it('should display text upon success', () => {
// Test implementation
});
});
});
Setting Up with beforeEach
Use beforeEach to set up common initial state or context needed across numerous tests to reduce repetitive setups.
describe('ToggleText', () => {
beforeEach(() => {
render(<ToggleText />);
});
// Tests follow
});
Similarly, utilize beforeAll, afterEach, and afterAll to manage shared setup and teardown logic.
FAQ
Can I use React Testing Library without Jest?
Yes, React Testing Library can be used with other test runners like Mocha. It's designed to be framework-agnostic, focusing on DOM testing utilities.
How do I mock functions in Jest?
Jest provides a jest.mock() function that allows you to intercept and define mock implementations or return values for dependencies your components rely upon.
What is the advantage of using Testing Library over Enzyme?
React Testing Library encourages testing from the user’s perspective, leading to more robust and meaningful tests by interacting with the actual DOM output.
Why does using waitFor matter in tests?
waitFor ensures that assertions are executed only after asynchronous operations complete, preventing brittle tests and intermittent failures.
