Featured image of post React Testing Library in a Nutshell

React Testing Library in a Nutshell

React Testing Library in a Nutshell

What is the React Testing Library?

React Testing Library (RTL) is like that one friend who actually listens when you talk. It doesn’t make assumptions about implementation details. Instead, it focuses on testing how users interact with your UI. This is a big deal because it means your tests won’t break just because you refactored some internal code.

Unlike Enzyme (which lets you poke around at the component’s internals like a nosy neighbor), RTL makes you test like a real user. That means finding elements like a user would (by text, role, or label) instead of relying on class names or structure.

Getting Started

First things first, install the library:

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

That’s it. No weird dependencies. No hours spent wondering why the testing framework is arguing with Webpack. Just install and go.

Writing a Simple Test

Let’s say we have a simple React component:

1
2
3
4
5
6
7
import React from 'react';

const Greeting = ({ name }) => {
  return <h1>Hello, {name}!</h1>;
};

export default Greeting;

Now, let’s test it using RTL:

1
2
3
4
5
6
7
8
import { render, screen } from '@testing-library/react';
import Greeting from './Greeting';

test('renders the greeting message', () => {
  render(<Greeting name="Alice" />);
  const greetingElement = screen.getByText(/Hello, Alice!/i);
  expect(greetingElement).toBeInTheDocument();
});

What’s Happening Here?

  1. We use render() to render the component in a virtual DOM.
  2. We use screen.getByText() to find the text “Hello, Alice!” just like a user would.
  3. We assert that the element is actually in the document. Simple, clean, and robust.

Common Queries in RTL

In React Testing Library, you don’t just use getByText() and call it a day. There are multiple ways to query elements:

  • getByText() – Finds an element by its text content.
  • getByRole() – Finds an element by its role (e.g., button, heading, textbox).
  • getByLabelText() – Finds an input field by its label.
  • getByPlaceholderText() – Finds an input field by its placeholder text.
  • getByTestId() – Finds an element by a custom data-testid attribute (use sparingly!).

Simulating User Events

What’s a UI without user interaction? Let’s say we have a button:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import React, { useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

export default Counter;

And here’s how you’d test it:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Counter from './Counter';

test('increments the count when button is clicked', async () => {
  render(<Counter />);
  
  const button = screen.getByRole('button', { name: /increment/i });
  await userEvent.click(button);
  
  expect(screen.getByText(/Count: 1/)).toBeInTheDocument();
});

Why Use userEvent?

React Testing Library includes fireEvent, but userEvent is more realistic. It mimics real user interactions like typing, clicking, and focusing.

Best Practices

  • Test Behavior, Not Implementation – Don’t test how the component works internally; test what the user sees.
  • Use screen Instead of Destructuring – It makes your tests more readable and requires fewer imports.
  • Prefer Queries Based on Accessibility – Use getByRole() and getByLabelText() to make tests future-proof.

Key Ideas

TopicSummary
PurposeTests components the way users interact with them
Installationnpm install @testing-library/react
QueriesgetByText, getByRole, getByLabelText, etc.
Simulating EventsuserEvent.click(), userEvent.type()
Best PracticesTest behavior, not implementation

References