Building Your Learning Module...
Getting things ready for you!
Find videos you like?
Save to resource drawer for future reference!
Component testing focuses on testing individual React components in isolation. You verify that each component renders correctly, handles props properly, and responds to user interactions as expected.
Unlike integration or E2E tests, component tests are fast and focused. They help you catch bugs early and refactor with confidence!
Run hundreds of tests in seconds without a real browser.
Test one component at a time in isolation from others.
Catch regressions immediately when you break something.
Test what users see and do, not how your component works internally. Here are the key areas:
This component shows different content based on props. Perfect example for testing conditional rendering and props!
<div id="root"></div>Loading preview...
Check if the component displays the correct name
import { render, screen } from '@testing-library/react';
import Greeting from './Greeting';
describe('Greeting Component - Props', () => {
test('displays the user name when logged in', () => {
render(<Greeting name="Sarah" isLoggedIn={true} />);
// Query for text that includes the name
const heading = screen.getByText(/Welcome, Sarah!/i);
expect(heading).toBeInTheDocument();
});
test('displays welcome message for different users', () => {
render(<Greeting name="John" isLoggedIn={true} />);
expect(screen.getByText(/Welcome, John!/i)).toBeInTheDocument();
});
test('displays generic text with different props', () => {
render(<Greeting name="Alice" isLoggedIn={true} />);
expect(screen.getByText(/Great to see you again/i)).toBeInTheDocument();
});
});Verify the component shows/hides content correctly
import { render, screen } from '@testing-library/react';
import Greeting from './Greeting';
describe('Greeting Component - Conditional Rendering', () => {
test('shows welcome message when logged in', () => {
render(<Greeting name="Sarah" isLoggedIn={true} />);
expect(screen.getByText(/Welcome, Sarah!/i)).toBeInTheDocument();
expect(screen.queryByText(/Please log in/i)).not.toBeInTheDocument();
});
test('shows login prompt when not logged in', () => {
render(<Greeting name="Sarah" isLoggedIn={false} />);
expect(screen.getByText(/Please log in/i)).toBeInTheDocument();
expect(screen.queryByText(/Welcome, Sarah!/i)).not.toBeInTheDocument();
});
test('shows correct message for logged out state', () => {
render(<Greeting name="John" isLoggedIn={false} />);
const loginPrompt = screen.getByText(/You must be logged in/i);
expect(loginPrompt).toBeInTheDocument();
});
});Verify state changes update the UI correctly
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';
describe('Counter Component - State', () => {
test('initializes with count of 0', () => {
render(<Counter />);
expect(screen.getByText(/Count: 0/i)).toBeInTheDocument();
});
test('increments count when button clicked', () => {
render(<Counter />);
const incrementBtn = screen.getByRole('button', { name: /increment/i });
fireEvent.click(incrementBtn);
expect(screen.getByText(/Count: 1/i)).toBeInTheDocument();
});
test('handles multiple increments', () => {
render(<Counter />);
const incrementBtn = screen.getByRole('button', { name: /increment/i });
// Click 3 times
fireEvent.click(incrementBtn);
fireEvent.click(incrementBtn);
fireEvent.click(incrementBtn);
expect(screen.getByText(/Count: 3/i)).toBeInTheDocument();
});
test('resets count to 0', () => {
render(<Counter />);
const incrementBtn = screen.getByRole('button', { name: /increment/i });
const resetBtn = screen.getByRole('button', { name: /reset/i });
// Increment then reset
fireEvent.click(incrementBtn);
fireEvent.click(incrementBtn);
fireEvent.click(resetBtn);
expect(screen.getByText(/Count: 0/i)).toBeInTheDocument();
});
});Verify buttons, inputs, and event handlers work
import { render, screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import SearchForm from './SearchForm';
describe('SearchForm Component - Events', () => {
test('calls onSearch with input value when submitted', () => {
const handleSearch = jest.fn();
render(<SearchForm onSearch={handleSearch} />);
const input = screen.getByRole('textbox');
const button = screen.getByRole('button', { name: /search/i });
// Type and submit
fireEvent.change(input, { target: { value: 'React' } });
fireEvent.click(button);
expect(handleSearch).toHaveBeenCalledWith('React');
expect(handleSearch).toHaveBeenCalledTimes(1);
});
test('clears input after submission', async () => {
const user = userEvent.setup();
render(<SearchForm onSearch={jest.fn()} />);
const input = screen.getByRole('textbox');
const button = screen.getByRole('button', { name: /search/i });
// Type and submit
await user.type(input, 'Testing');
await user.click(button);
expect(input).toHaveValue('');
});
test('does not call onSearch with empty value', () => {
const handleSearch = jest.fn();
render(<SearchForm onSearch={handleSearch} />);
const button = screen.getByRole('button', { name: /search/i });
fireEvent.click(button);
// Should not be called with empty string
expect(handleSearch).not.toHaveBeenCalled();
});
});React Testing Library provides several ways to query elements. Choose the right one for accessibility and reliability:
Returns element or throws error if not found
✅ Best for elements that should exist
Returns null if not found (no error)
✅ Best for checking if element is NOT there
Waits for element to appear (async)
✅ Best for elements that appear after loading
Focus on what the user sees and does. If you refactor and tests still pass but the component is broken, you're testing the wrong things.
Prefer getByRole and getByLabelText. They ensure your app is accessible and tests mimic real users.
Each test should verify one specific behavior. This makes failures easy to debug and tests easy to understand.
React Testing Library automatically cleans up after each test. But if you add timers or global listeners, clean them up in afterEach.
Verify components render correctly with different props using render() and screen queries.
Pass different props and verify the component displays the correct data.
Use fireEvent or userEvent to trigger state changes and verify UI updates.
Simulate clicks, typing, and form submissions with fireEvent or userEvent.
Use getBy* for existing elements, queryBy* for absence, findBy* for async.
Always test from the user's perspective - what they see and do!
You now know how to test component rendering, props, state, and events! Next topics: