Документация для миграции

Введение в миграцию

Миграция на React Testing Library (RTL) — важный шаг для улучшения качества тестирования React-компонентов. RTL предлагает подход, ориентированный на пользовательские действия, что помогает создавать более реалистичные тесты по сравнению с предыдущими библиотеками, такими как Enzyme. RTL фокусируется на взаимодействиях с компонентами так, как это делает конечный пользователь, а не через внутренние детали реализации.

Зачем мигрировать на React Testing Library?

Использование RTL позволяет повысить надежность тестов, улучшить читаемость кода и упростить его поддержку. Главная цель RTL — избежать привязки тестов к внутренним механизмам компонента, что делает тесты менее уязвимыми при изменениях в коде.

Некоторые из ключевых преимуществ RTL:

  • Тесты, приближенные к реальному поведению: библиотека фокусируется на взаимодействиях, таких как клик мышью, ввод текста, изменение состояния.
  • Чтение компонентов как пользователь: тесты больше не зависят от внутренних реализаций (например, стейта или методов жизненного цикла), что повышает их устойчивость к изменениям.
  • Минимизация использования методов, связанных с внутренним состоянием: такие методы, как setState или прямой доступ к DOM, становятся излишними.

Подготовка к миграции

Перед тем как приступить к миграции, необходимо убедиться, что проект уже содержит базовые тесты с использованием старой библиотеки тестирования (например, с Enzyme). Чтобы провести миграцию, потребуется тщательно оценить текущие тесты и идентифицировать те, которые можно адаптировать под React Testing Library.

Шаги миграции на React Testing Library

  1. Установка React Testing Library и зависимостей

    Первым шагом в миграции является установка необходимых пакетов. Для этого в проект нужно добавить React Testing Library и @testing-library/jest-dom (для работы с удобными матчерами).

    npm install --save-dev @testing-library/react @testing-library/jest-dom
  2. Подготовка конфигурации Jest

    Если в проекте используется Jest (что является стандартом для большинства React-приложений), то важно настроить соответствующие глобальные методы и матчеры. Для этого можно добавить следующую строку в файл конфигурации Jest или в отдельный файл настройки, который будет импортироваться перед каждым тестом.

    import '@testing-library/jest-dom/extend-expect';

    Это позволит использовать такие утверждения, как .toBeInTheDocument(), .toHaveTextContent(), и другие методы, которые значительно упрощают написание тестов.

  3. Обновление тестов: заменяем Enzyme на React Testing Library

    Преобразование тестов с Enzyme в RTL включает в себя несколько ключевых изменений, которые касаются способов рендеринга компонентов, взаимодействия с элементами и проверки результатов.

    • Рендеринг компонентов

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

      Пример кода:

      Enzyme (до миграции):

      import { shallow } from 'enzyme';
      import MyComponent from './MyComponent';
      
      it('should render correctly', () => {
        const wrapper = shallow(<MyComponent />);
        expect(wrapper.find('h1').text()).toBe('Hello, world!');
      });

      React Testing Library (после миграции):

      import { render, screen } from '@testing-library/react';
      import MyComponent from './MyComponent';
      
      it('should render correctly', () => {
        render(<MyComponent />);
        expect(screen.getByText('Hello, world!')).toBeInTheDocument();
      });
    • Работа с событиями

      В Enzyme для имитации пользовательских событий использовался метод simulate. В RTL взаимодействие с компонентом осуществляется с помощью fireEvent или userEvent (предпочтительнее для симуляции реальных пользовательских действий).

      Пример кода:

      Enzyme (до миграции):

      const wrapper = shallow(<MyComponent />);
      wrapper.find('button').simulate('click');
      expect(wrapper.state('clicked')).toBe(true);

      React Testing Library (после миграции):

      import { fireEvent, render } from '@testing-library/react';
      import MyComponent from './MyComponent';
      
      it('should update state on button click', () => {
        const { getByText } = render(<MyComponent />);
        fireEvent.click(getByText('Click me'));
        expect(getByText('You clicked!')).toBeInTheDocument();
      });
  4. Использование пользовательских событий

    Для имитации пользовательских взаимодействий предпочтительнее использовать userEvent, так как эта библиотека более точно симулирует поведение пользователя, в отличие от fireEvent, который использует более низкоуровневые события.

    Пример:

    import userEvent from '@testing-library/user-event';
    
    it('should type in the input field', async () => {
      const { getByRole } = render(<MyComponent />);
      const input = getByRole('textbox');
      await userEvent.type(input, 'Hello');
      expect(input).toHaveValue('Hello');
    });
  5. Тестирование асинхронных операций

    React Testing Library активно использует методы для тестирования асинхронных операций, такие как findBy, waitFor, и waitForElementToBeRemoved.

    Пример:

    it('should load data asynchronously', async () => {
      const { getByText } = render(<MyComponent />);
      await waitFor(() => getByText('Data loaded'));
      expect(getByText('Data loaded')).toBeInTheDocument();
    });
  6. Проверка визуальных изменений

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

    Пример:

    it('should show a loading message while fetching data', () => {
      const { getByText } = render(<MyComponent />);
      expect(getByText('Loading...')).toBeInTheDocument();
    });
  7. Миграция тестов с компонентами, использующими контекст

    В случае, если компоненты используют контекст (например, React Context API), важно также адаптировать тесты, чтобы они корректно обрабатывали контекстное состояние. Для этого используется компонент-поставщик контекста, оборачивающий тестируемые компоненты.

    Пример:

    import { MyContextProvider } from './MyContext';
    
    it('should consume context value', () => {
      const { getByText } = render(
        <MyContextProvider>
          <MyComponent />
        </MyContextProvider>
      );
      expect(getByText('Context value: true')).toBeInTheDocument();
    });

Советы по миграции

  • Начните с самых простых компонентов. Постепенно переходите к более сложным, содержащим состояния или эффекты.
  • При необходимости адаптируйте свои тесты к современным возможностям React, например, для использования хуков и контекстов.
  • Используйте debug() из RTL для вывода текущего состояния DOM во время тестов. Это поможет в процессе отладки.

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