React Testing Library lets you test React components by interacting with them in a way that resembles how a user would.
Let’s say you have a simple counter component:
// Counter.jsx
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
export default Counter;
Here’s how you’d test it with Jest and React Testing Library:
// Counter.test.js
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';
test('increments count when button is clicked', () => {
render(<Counter />);
const buttonElement = screen.getByText(/Click me/i);
const paragraphElement = screen.getByText(/You clicked 0 times/i);
expect(paragraphElement).toBeInTheDocument();
fireEvent.click(buttonElement);
expect(paragraphElement).toHaveTextContent('You clicked 1 times');
});
This test renders the Counter component, finds the "Click me" button and the paragraph displaying the count, asserts that the initial count is 0, simulates a click on the button, and then asserts that the paragraph now displays "You clicked 1 times".
The core idea is to query for elements as a user would. React Testing Library provides several query methods:
getByRole: Finds elements by their ARIA role (e.g.,button,textbox,dialog). This is generally the most accessible and recommended way to query.getByLabelText: Finds form elements by their associated<label>text.getByPlaceholderText: Finds form elements by theirplaceholderattribute.getByText: Finds elements by their text content.getByDisplayValue: Finds form elements by their current value.getByAltText: Finds elements with analtattribute (like<img>or<area>).getByTitle: Finds elements with atitleattribute.getByTestId: Finds elements by adata-testidattribute. This is a last resort, as it doesn’t map to user-facing attributes.
Each of these get* methods also has query* and find* variants. query* methods return null if an element isn’t found (useful for asserting absence), while find* methods return a Promise and retry until the element appears or the timeout is reached (useful for asynchronous operations).
When testing, you’ll often need to simulate user interactions. fireEvent is the primary way to do this. Common events include:
fireEvent.click(element)fireEvent.change(element, { target: { value: 'new value' } })fireEvent.keyDown(element, { key: 'Enter', code: 'Enter' })
The beauty of this approach is that your tests become more robust. If you refactor the internal implementation of your component (e.g., change state management from useState to useReducer) but the user-facing behavior remains the same, your tests will still pass. This is because you’re not testing implementation details, but rather the observable output and interactions.
To set this up in your project, you’ll typically install Jest and @testing-library/react:
npm install --save-dev jest @testing-library/react @testing-library/jest-dom
# or
yarn add --dev jest @testing-library/react @testing-library/jest-dom
Then, configure Jest. For Create React App projects, this is usually handled out of the box. For custom setups, you might need a jest.config.js file. You’ll also want to set up @testing-library/jest-dom for helpful DOM assertion matchers:
// jest.setup.js
import '@testing-library/jest-dom';
And configure Jest to use this setup file in your jest.config.js:
// jest.config.js
module.exports = {
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
testEnvironment: 'jsdom',
// ... other configurations
};
The most surprising benefit of testing this way is how it forces you to write more accessible components. Because you’re encouraged to query by ARIA roles and labels, you naturally think about how a screen reader or other assistive technology would perceive your component.
When you’re done testing your component’s basic functionality and interactions, you’ll naturally start thinking about how to test components that depend on asynchronous operations, like fetching data.