Writing Unit Tests for Functional Components0%
Writing Unit Tests for Functional Components
Beginner12 min read•Updated: Jul 11, 2026
Writing Unit Tests for Functional Components
1. Anatomy of a Component Unit Test
A standard unit test in React follows the AAA Pattern:
- 1Arrange: Render the component using
render(<MyComponent />)and prepare props or mocks. - 2Act: Simulate user interactions (typing, clicking, toggling) using
@testing-library/user-event. - 3Assert: Verify that the expected DOM elements, text, or callbacks responded correctly.
Output
[Arrange: render(<Counter />)] ──► [Act: user.click(button)] ──► [Assert: expect(text).toHaveTextContent('1')]
2. Complete Example: Testing a Course Card Component
Let's write a comprehensive test suite for an interactive course card:
JSX
// src/components/CourseCard.jsx
import React from 'react';
export default function CourseCard({ course, onEnroll }) {
return (
<div className="course-card">
<h3 data-testid="course-title">{course.title}</h3>
<p>{course.description}</p>
<span className="price">${course.price}</span>
<button
type="button"
disabled={course.isSoldOut}
onClick={() => onEnroll(course.id)}
>
{course.isSoldOut ? 'Sold Out' : 'Enroll Now'}
</button>
</div>
);
}
The Vitest Test File:
JSX
// src/components/__tests__/CourseCard.test.jsx
import { describe, it, expect, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import CourseCard from '../CourseCard';
describe('CourseCard Component', () => {
const mockCourse = {
id: 'c-101',
title: 'React.js Mastery',
description: 'Master modern frontend web engineering.',
price: 99,
isSoldOut: false
};
it('renders course information correctly', () => {
render(<CourseCard course={mockCourse} onEnroll={vi.fn()} />);
// Assert title and price are in the document
expect(screen.getByRole('heading', { level: 3 })).toHaveTextContent('React.js Mastery');
expect(screen.getByText(/Master modern frontend/i)).toBeInTheDocument();
expect(screen.getByText('$99')).toBeInTheDocument();
});
it('invokes onEnroll callback with course ID when Enroll button is clicked', async () => {
// vi.fn() creates a mock spy function in Vitest
const handleEnroll = vi.fn();
const user = userEvent.setup();
render(<CourseCard course={mockCourse} onEnroll={handleEnroll} />);
const enrollButton = screen.getByRole('button', { name: /enroll now/i });
await user.click(enrollButton);
// Verify spy function was called exactly once with 'c-101'
expect(handleEnroll).toHaveBeenCalledTimes(1);
expect(handleEnroll).toHaveBeenCalledWith('c-101');
});
it('disables the button when the course is sold out', () => {
const soldOutCourse = { ...mockCourse, isSoldOut: true };
render(<CourseCard course={soldOutCourse} onEnroll={vi.fn()} />);
const button = screen.getByRole('button', { name: /sold out/i });
expect(button).toBeDisabled();
});
});
3. Why userEvent Over fireEvent
Notice line 27: const user = userEvent.setup(); await user.click(button);.
fireEvent.click(): Dispatches a synthetic DOM event instantly without simulating hover, focus, or active state sequences.userEvent.click(): Faithfully simulates what a real human does: moves cursor, fires mouseover, hovers, focuses the element, clicks, and fires blur upon leaving.
Always prefer @testing-library/user-event for interaction testing!
Interactive Quiz
Practice Quiz
Question 1
What is the recommended tool for simulating user clicks and keystrokes in React Testing Library?
Question 2
What Vitest function creates a mock spy function to verify if a callback prop was invoked?
Question 3
What matcher from
@testing-library/jest-dom asserts that a button has its HTML disabled attribute active?Question 4
Why must calls to
userEvent methods (like await user.click(...) or await user.type(...)) be awaited?Question 5
In the AAA testing pattern, what does the "Act" phase represent?
Next Lesson
Simulating User Events and Testing Asynchronous Updates
Continue learning with hands-on practice, examples, and exercises in the upcoming topic.
Related Lessons
Practice Quiz
Test your understanding of this lesson with 5 questions. Each question has one correct answer.