Integration Testing with React Testing Library
Render components in tests, fire events, and assert on visible output not implementation.
Integration Testing with React Testing Library is a free React Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the React Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
What Is React Testing Library?
React Testing Library (RTL) encourages testing from the user's perspective: render components, interact with them as a user would, and assert on visible output rather than implementation details.
Setup
RTL is included with Create React App and Vite's React template. For manual setup, install @testing-library/react and @testing-library/jest-dom.
// package.json
// "@testing-library/react": "^14.0.0",
// "@testing-library/jest-dom": "^6.0.0"
// setupTests.ts
import '@testing-library/jest-dom';Rendering a Component
render() mounts a component in a virtual DOM. It returns query functions scoped to the rendered output.
import { render, screen } from '@testing-library/react';
import { Button } from './Button';
test('renders button label', () => {
render(<Button label="Submit" onClick={() => {}} />);
expect(screen.getByText('Submit')).toBeInTheDocument();
});Querying Elements
Prefer queries in this order: getByRole > getByLabelText > getByPlaceholderText > getByText. Role queries match accessibility semantics.
// Best: query by role
const button = screen.getByRole('button', { name: /submit/i });
// Good: query by label (for form inputs)
const input = screen.getByLabelText('Email address');
// Acceptable: query by text
const heading = screen.getByText('Welcome');Firing Events
Use userEvent from @testing-library/user-event (preferred) or fireEvent (basic). userEvent simulates real browser interactions including focus and blur.
import userEvent from '@testing-library/user-event';
test('types into input', async () => {
const user = userEvent.setup();
render(<SearchBar />);
const input = screen.getByRole('textbox');
await user.type(input, 'react');
expect(input).toHaveValue('react');
});Testing a Form Submission
Render the form, fill inputs with userEvent.type, click submit, and assert on the result (success message, navigation, or callback call).
test('submits the login form', async () => {
const onLogin = jest.fn();
const user = userEvent.setup();
render(<LoginForm onLogin={onLogin} />);
await user.type(screen.getByLabelText('Email'), 'test@example.com');
await user.type(screen.getByLabelText('Password'), 'password123');
await user.click(screen.getByRole('button', { name: /log in/i }));
expect(onLogin).toHaveBeenCalledWith({ email: 'test@example.com', password: 'password123' });
});Asserting Visibility
jest-dom matchers like toBeInTheDocument(), toBeVisible(), and toHaveValue() make assertions readable.
expect(screen.getByText('Error: required')).toBeInTheDocument();
expect(screen.getByRole('dialog')).toBeVisible();
expect(screen.getByRole('textbox')).toHaveValue('hello');
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();Testing Conditional Rendering
Use queryBy (returns null when absent) to assert an element is not rendered. Use getBy when you expect it to be present.
test('shows error message when validation fails', async () => {
const user = userEvent.setup();
render(<SignUpForm />);
await user.click(screen.getByRole('button', { name: /submit/i }));
expect(screen.getByText('Email is required')).toBeInTheDocument();
});Wrapping with Providers
Most components need context or router. Wrap them in providers inside the render call, or create a custom renderWithProviders helper.
function renderWithProviders(ui: React.ReactElement) {
return render(
<QueryClientProvider client={new QueryClient()}>
<MemoryRouter>
{ui}
</MemoryRouter>
</QueryClientProvider>
);
}
test('shows user data', () => {
renderWithProviders(<UserProfile />);
});Testing State Changes
Act on the component, then assert the new state is reflected in the UI. RTL wraps most interactions in act automatically when using userEvent.
test('counter increments', async () => {
const user = userEvent.setup();
render(<Counter />);
expect(screen.getByText('Count: 0')).toBeInTheDocument();
await user.click(screen.getByRole('button', { name: /increment/i }));
expect(screen.getByText('Count: 1')).toBeInTheDocument();
});Screen Debug Helper
Call screen.debug() to print the current rendered HTML to the console — useful when a query fails and you need to see what's actually rendered.
test('debugging a failing query', () => {
render(<MyComponent />);
screen.debug(); // prints rendered HTML
screen.getByRole('button', { name: /save/i }); // then inspect output
});Quick Check
Which query should you prefer when testing a button in React Testing Library?
Recap
RTL tests render components and interact with them via role-based queries and userEvent. Wrap components in required providers, use jest-dom matchers for readable assertions, and prefer queryBy when asserting absence.
Frequently asked questions
Is the “Integration Testing with React Testing Library” lesson free?
Yes — the full text of “Integration Testing with React Testing Library” is free to read here on the web, and the React Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the React Academy course, upgrade to CoddyKit PRO.
What will I learn in “Integration Testing with React Testing Library”?
Render components in tests, fire events, and assert on visible output not implementation. You practise React Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start React Academy?
No prior experience is required. React Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Integration Testing with React Testing Library” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this React Academy lesson?
Yes. Every React Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Integration Testing with React Testing Library
- Testing Async UI & API Calls with MSW
- Getting Started with Playwright for React
- Testing Forms & User Flows End-to-End