Component tests with testing library

Testing Library has one philosophy: test the component the way a user uses it. Query by role, label, or text. Trigger real user events. Assert on what is on the screen, not on implementation details. Tests written this way survive refactors because they only care about behavior.

src/components/Counter.test.tsx
tsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect } from 'vitest';
import { Counter } from './Counter';

describe('Counter', () => {
  it('increments when the plus button is clicked', async () => {
    render(<Counter />);

    expect(screen.getByText('Count: 0')).toBeInTheDocument();

    const plus = screen.getByRole('button', { name: /\+/ });
    await userEvent.click(plus);

    expect(screen.getByText('Count: 1')).toBeInTheDocument();
  });
});

A component test that clicks a button and asserts on what the user would see.

Notice what the test does not do. It does not inspect state. It does not snapshot the DOM. It does not call internal functions. It renders the component, clicks a button the way a user would, and asserts on text the user would see. Refactor the Counter internals all you want and this test still passes as long as the behavior holds.

Quiz: Quiz

Loading practice…