Как написать тест для React компонента с использованием Jest?react-73

Написание тестов для React-компонентов с использованием Jest — ключевой навык современного фронтенд-разработчика. Рассмотрим полный процесс от настройки до продвинутых техник.

1. Подготовка среды

Для тестирования React-компонентов нам понадобятся:

  • Jest (уже включен в create-react-app)
  • React Testing Library (RTL) - рекомендованный инструмент для тестирования компонентов

Установка (если проект не на CRA):

npm install --save-dev @testing-library/react @testing-library/jest-dom

2. Базовый тест компонента

Рассмотрим компонент Button.js:

function Button({ onClick, children }) {
  return <button onClick={onClick}>{children}</button>;
}

Тест для этого компонента (Button.test.js):

import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';

test('renders button with text and handles click', () => {
  // 1. Мок-функция для обработчика
  const handleClick = jest.fn();

  // 2. Рендер компонента
  render(<Button onClick={handleClick}>Click me</Button>);

  // 3. Поиск элемента
  const buttonElement = screen.getByText(/click me/i);

  // 4. Проверка наличия
  expect(buttonElement).toBeInTheDocument();

  // 5. Симуляция клика
  fireEvent.click(buttonElement);

  // 6. Проверка вызова обработчика
  expect(handleClick).toHaveBeenCalledTimes(1);
});

3. Тестирование разных аспектов

Проверка пропсов

test('applies correct className', () => {
  render(<Button className="primary">Submit</Button>);
  const button = screen.getByRole('button');
  expect(button).toHaveClass('primary');
});

Тестирование состояний

Для компонента с состоянием:

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <span data-testid="count">{count}</span>
      <button onClick={() => setCount(c => c + 1)}>Increment</button>
    </div>
  );
}

test('increments counter', () => {
  render(<Counter />);
  const countElement = screen.getByTestId('count');
  const button = screen.getByText('Increment');

  expect(countElement).toHaveTextContent('0');

  fireEvent.click(button);
  expect(countElement).toHaveTextContent('1');
});

4. Тестирование хуков

Для тестирования кастомных хуков используем @testing-library/react-hooks:

npm install @testing-library/react-hooks

Пример теста:

import { renderHook, act } from '@testing-library/react-hooks';
import useCounter from './useCounter';

test('should increment counter', () => {
  const { result } = renderHook(() => useCounter());

  act(() => {
    result.current.increment();
  });

  expect(result.current.count).toBe(1);
});

5. Снапшот-тестирование

Полезно для обнаружения неожиданных изменений в компонентах:

import renderer from 'react-test-renderer';

test('Button snapshot', () => {
  const tree = renderer
    .create(<Button>Save</Button>)
    .toJSON();
  expect(tree).toMatchSnapshot();
});

6. Тестирование асинхронного поведения

Пример теста для компонента с API-запросом:

import { render, screen, waitFor } from '@testing-library/react';
import UserProfile from './UserProfile';

test('loads and displays user data', async () => {
  // Мокаем API-запрос
  global.fetch = jest.fn(() =>
    Promise.resolve({
      json: () => Promise.resolve({ name: 'John Doe' }),
    })
  );

  render(<UserProfile userId="123" />);

  // Проверяем состояние загрузки
  expect(screen.getByText(/loading/i)).toBeInTheDocument();

  // Ждем появления данных
  await waitFor(() => {
    expect(screen.getByText('John Doe')).toBeInTheDocument();
  });
});

7. Лучшие практики

  1. Тестируйте поведение, а не реализацию
    Фокус на том, что делает компонент, а не как он устроен внутри

  2. Используйте правильные queries
    Предпочитайте getByRole вместо getByTestId где возможно

  3. Избегайте лишних снапшотов
    Снапшоты полезны, но не должны заменять осмысленные assertions

  4. Держите тесты изолированными
    Каждый тест должен работать независимо от других

Резюмируем

Для тестирования React-компонентов с Jest мы используем комбинацию Jest и React Testing Library. Основные шаги: рендер компонента, поиск элементов в DOM, взаимодействие с элементами и проверка результатов. Правильно написанные тесты помогают находить ошибки на ранних этапах и делают код более надежным.