Adzbyte
All Articles
DevelopmentTutorials

React Testing Library Patterns That Actually Work

Adrian Saycon
Adrian Saycon
January 29, 20261 min read
React Testing Library Patterns That Actually Work

React Testing Library encourages testing behavior over implementation. But without good patterns, tests become brittle and hard to maintain. Here are the patterns I have found most effective.

Test What Users See, Not Implementation

Query elements the way users find them — by role, label, and text content:

// Bad: testing implementation details
const button = container.querySelector(".submit-btn");

// Good: testing what users see
const button = screen.getByRole("button", { name: /submit/i });
const input = screen.getByLabelText(/email address/i);
const heading = screen.getByRole("heading", { name: /contact us/i });

The Arrange-Act-Assert Pattern

Every test follows the same structure:

test("shows error when email is invalid", async () => {
    // Arrange: render component
    render(<ContactForm />);

    // Act: simulate user behavior
    await userEvent.type(screen.getByLabelText(/email/i), "not-an-email");
    await userEvent.click(screen.getByRole("button", { name: /submit/i }));

    // Assert: verify the outcome
    expect(screen.getByText(/please enter a valid email/i)).toBeInTheDocument();
});

Mock External Dependencies, Not Components

Mock API calls and browser APIs, but render real components:

// Mock the API, not the component
vi.mock("../lib/api", () => ({
    fetchProjects: vi.fn().mockResolvedValue([
        { id: 1, title: "Project A" },
        { id: 2, title: "Project B" },
    ]),
}));

IntersectionObserver Mock

For components with scroll-triggered animations, mock the observer:

beforeEach(() => {
    const mockIntersectionObserver = vi.fn();
    mockIntersectionObserver.mockReturnValue({
        observe: vi.fn(),
        unobserve: vi.fn(),
        disconnect: vi.fn(),
    });
    window.IntersectionObserver = mockIntersectionObserver;
});

These patterns keep tests focused, readable, and resistant to refactoring. If a test breaks because you renamed a CSS class, that test was testing the wrong thing.

Adrian Saycon

Written by

Adrian Saycon

A developer with a passion for emerging technologies, Adrian Saycon focuses on transforming the latest tech trends into great, functional products.

Discussion (0)

Sign in to join the discussion

No comments yet. Be the first to share your thoughts.