Create React App preset

Create React App — это популярный инструмент для быстрого старта в разработке React-приложений. Он предоставляет базовую настройку для проекта, избавляя разработчика от необходимости конфигурировать различные сборщики, компиляторы и другие инструменты. Для тестирования React-компонентов в таких проектах часто используется React Testing Library (RTL). Эта библиотека ориентирована на поведение компонентов и помогает писать тесты, которые имитируют реальное взаимодействие с пользовательским интерфейсом. В контексте создания тестов с использованием RTL и Create React App, существует несколько особенностей, которые стоит учитывать.

Настройка тестовой среды

После создания проекта с помощью npx create-react-app тестовая среда уже настроена по умолчанию. Включение React Testing Library происходит автоматически, так как Create React App использует Jest как тестовый фреймворк, который интегрируется с RTL.

Основная структура проекта будет включать папку src с компонентами, и папку __tests__ (или тесты могут быть расположены рядом с компонентами в отдельных файлах с расширением .test.js).

Пример базовой структуры проекта:

/my-app
  /node_modules
  /public
  /src
    /components
      MyComponent.js
      MyComponent.test.js
  /__tests__
    /App.test.js
  package.json

Тесты для компонентов помещаются в файлы с расширением .test.js рядом с самими компонентами. Это помогает легче поддерживать тесты, особенно когда проект растет.

Основные концепции и функции React Testing Library

React Testing Library делает акцент на том, чтобы тесты были максимально близки к тому, как компоненты работают в реальном браузере. RTL помогает писать тесты, используя такие принципы, как:

  • Проверка поведения компонентов, а не их внутренней реализации.
  • Использование селекторов, похожих на реальные, например, getByText, getByRole, getByLabelText, которые имитируют взаимодействие с DOM.
  • Тестирование как пользователь, а не как разработчик, который знает внутреннюю структуру компонента.

Установка и интеграция в проект Create React App

Для проектов, созданных через create-react-app, не нужно дополнительно устанавливать React Testing Library, так как она уже входит в зависимости по умолчанию. Тем не менее, если по каким-то причинам библиотека не установлена, можно добавить её вручную:

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

Написание тестов для компонентов

Для того чтобы протестировать компонент с помощью RTL, важно использовать функции из библиотеки для рендеринга компонента и взаимодействия с ним. Самый базовый пример выглядит следующим образом:

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

test('отображение текста', () => {
  render(<MyComponent />);
  const linkElement = screen.getByText(/привет, мир/i);
  expect(linkElement).toBeInTheDocument();
});

Здесь:

  • render(<MyComponent />) рендерит компонент в тестовом DOM.
  • screen.getByText(/привет, мир/i) ищет элемент с текстом “привет, мир” (используется регулярное выражение для поиска).
  • expect(linkElement).toBeInTheDocument() проверяет, что элемент присутствует в документе.

Асинхронные тесты

Важной частью тестирования с React Testing Library является работа с асинхронными операциями, такими как запросы к API или динамическая загрузка данных. RTL предоставляет утилиту findBy, которая позволяет ожидать появления элемента в DOM через асинхронные операции.

Пример асинхронного теста:

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

test('ожидание данных с сервера', async () => {
  render(<MyComponent />);
  
  // Предположим, что компонент делает запрос к серверу и отображает данные
  const dataElement = await screen.findByText(/загруженные данные/i);

  expect(dataElement).toBeInTheDocument();
});

Здесь:

  • screen.findByText(/загруженные данные/i) ищет элемент с текстом “загруженные данные” и ждет, пока он появится в DOM.
  • await необходим для того, чтобы дождаться завершения асинхронной операции.

Мокающие запросы и серверы

Если компонент зависит от данных с сервера, то для тестирования такого компонента можно использовать библиотеку msw (Mock Service Worker), которая позволяет мокировать API-запросы, не обращаясь к реальному серверу. Для этого можно добавить следующий код в тест:

import { render, screen, waitFor } from '@testing-library/react';
import { server, rest } from 'msw';
import MyComponent from './MyComponent';

// Мокаем API-запрос
beforeAll(() => server.listen());
afterAll(() => server.close());
afterEach(() => server.resetHandlers());

test('загрузка данных с мока', async () => {
  server.use(
    rest.get('/api/data', (req, res, ctx) => {
      return res(ctx.json({ message: 'Мокированные данные' }));
    })
  );
  
  render(<MyComponent />);
  
  const dataElement = await screen.findByText(/Мокированные данные/i);
  expect(dataElement).toBeInTheDocument();
});

Здесь:

  • server.use позволяет мокировать конкретный запрос.
  • msw позволяет контролировать поведение API-запросов, делая тесты более стабильными и изолированными от реального бэкенда.

Селекторы и их роль в тестах

Важным аспектом при написании тестов является выбор правильных селекторов для поиска элементов в DOM. React Testing Library рекомендует использовать семантические селекторы, такие как getByRole, getByLabelText и getByPlaceholderText, которые лучше всего отражают взаимодействие пользователя с интерфейсом.

Пример с использованием getByRole:

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

test('кнопка отправки формы', () => {
  render(<MyComponent />);
  
  const submitButton = screen.getByRole('button', { name: /отправить/i });
  expect(submitButton).toBeInTheDocument();
});

Здесь:

  • getByRole используется для поиска кнопки с ролью button и текстом “отправить”.
  • Это предпочтительный способ поиска элементов, так как он отражает реальные пользовательские действия.

Обработка событий в тестах

React Testing Library предоставляет удобные методы для симуляции пользовательских действий, таких как клики или ввод текста. Например, для имитации клика по кнопке можно использовать метод fireEvent.click:

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

test('клик по кнопке изменяет состояние', () => {
  render(<MyComponent />);
  
  const button = screen.getByRole('button', { name: /отправить/i });
  fireEvent.click(button);

  const message = screen.getByText(/успешно отправлено/i);
  expect(message).toBeInTheDocument();
});

Здесь:

  • fireEvent.click(button) имитирует клик по кнопке.
  • После клика проверяется, что в DOM появилось сообщение об успешной отправке.

Важные моменты при написании тестов

  1. Тестирование поведения, а не реализации: Не стоит проверять внутреннюю логику компонента, такую как стейт или методы. Лучше фокусироваться на том, что компонент отображает и как он реагирует на действия пользователя.
  2. Изоляция тестов: Тесты должны быть независимыми. Использование моков и шпионов помогает изолировать компоненты от внешних зависимостей.
  3. Чтение ошибок: В случае ошибки важно читать сообщения в консоли и понимать, какой элемент не был найден или не соответствует ожиданиям.

Вместо того чтобы сосредотачиваться на внутренней реализации компонента, React Testing Library направлена на тестирование конечного результата и поведения интерфейса.