Тестирование React компонентов

Для работы с React компонентами с помощью Cypress используется комбинация Cypress, Cypress Testing Library и, при необходимости, @cypress/react. Cypress позволяет запускать тесты как в браузере, так и в headless-режиме, обеспечивая высокую стабильность и наглядность.

  1. Установка Cypress и зависимостей:
npm install cypress @testing-library/cypress @cypress/react --save-dev
  1. Инициализация Cypress:
npx cypress open

Это создаст структуру папок cypress/, где будут храниться тесты, фикстуры и конфигурация.

  1. Подключение плагинов:

В файле cypress/support/e2e.js необходимо импортировать функции библиотеки:

import '@testing-library/cypress/add-commands';

Для тестирования React компонентов через @cypress/react используется отдельная конфигурация:

import { mount } from '@cypress/react';

Основные принципы тестирования React компонентов

Изоляция компонентов. Каждый тест должен проверять отдельный компонент без зависимости от состояния всего приложения. Использование моков и стаба функций позволяет контролировать поведение зависимостей.

Селекторы и доступность. Cypress Testing Library поощряет использование селекторов на основе текста, ролей и атрибутов ARIA, а не CSS-классов. Это делает тесты более устойчивыми к изменениям стилей.

Пример:

cy.findByRole('button', { name: /добавить/i }).click();
cy.findByText(/элемент добавлен/i).should('exist');

Mounting React компонентов

Метод mount позволяет рендерить компонент в изолированной среде Cypress. Это аналог unit-тестирования компонентов в Jest, но с возможностью взаимодействия через браузер.

import MyButton from '../. ./src/components/MyButton';

it('клик по кнопке вызывает коллбек', () => {
  const onCl ick = cy.stub();
  mount(<MyButton onCl ick={onClick}>Нажми меня</MyButton>);
  cy.findByText(/Нажми меня/i).click().then(() => {
    expect(onClick).to.have.been.calledOnce;
  });
});

Особенности:

  • mount создает полноценный DOM для компонента.
  • Можно тестировать состояние, пропсы и события.
  • Поддерживаются хук состояния (useState, useEffect) и контексты (useContext).

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

React компоненты часто содержат внутреннее состояние. Cypress позволяет отслеживать изменения через DOM и имитацию пользовательских действий.

Пример теста формы:

import LoginForm from '../. ./src/components/LoginForm';

it('отображает ошибку при пустом вводе', () => {
  mount(<LoginForm />);
  cy.findByRole('button', { name: /войти/i }).click();
  cy.findByText(/поле не может быть пустым/i).should('exist');
});

it('вызывает onSubmit при корректном вводе', () => {
  const onSub mit = cy.stub();
  mount(<LoginForm onSub mit={onSubmit} />);
  cy.findByLabelText(/логин/i).type('user');
  cy.findByLabelText(/пароль/i).type('12345');
  cy.findByRole('button', { name: /войти/i }).click().then(() => {
    expect(onSubmit).to.have.been.calledWith({ username: 'user', password: '12345' });
  });
});

Моки API и внешних данных

Компоненты React часто зависят от внешних API. Cypress позволяет создавать стабы и моки через cy.intercept.

cy.intercept('GET', '/api/users', {
  statusCode: 200,
  body: [{ id: 1, name: 'Иван' }],
}).as('getUsers');

mount(<UserList />);
cy.wait('@getUsers');
cy.findByText(/Иван/i).should('exist');

Преимущества:

  • Возможность контролировать ответы API.
  • Тесты становятся детерминированными.
  • Исключается зависимость от внешнего сервера.

Комбинированные тесты: состояние + взаимодействие + API

Можно тестировать полный сценарий компонента:

cy.intercept('POST', '/api/login', {
  statusCode: 200,
  body: { token: 'abc123' },
}).as('login');

mount(<LoginForm />);
cy.findByLabelText(/логин/i).type('user');
cy.findByLabelText(/пароль/i).type('12345');
cy.findByRole('button', { name: /войти/i }).click();
cy.wait('@login');
cy.findByText(/успешный вход/i).should('exist');

Такой подход объединяет проверку UI, состояния и сетевых взаимодействий в одном тесте.

Практики написания стабильных тестов

  • Использовать ARIA-ролей и текстовые селекторы вместо CSS-классов.
  • Разделять тесты на unit и integration, чтобы избегать излишней зависимости компонентов.
  • Всегда мокать внешние данные.
  • Проверять конечный результат действий через DOM, а не через внутренние состояния компонента.

Интеграция с CI/CD

Cypress легко интегрируется с системами непрерывной интеграции. Использование headless-режима (cypress run --headless) позволяет запускать тесты автоматически при сборке. Дополнительно можно сохранять видео и скриншоты при падении теста для удобного анализа.

npx cypress run --headless --record --key <project-record-key>

Расширение возможностей

  • Cypress Studio позволяет создавать тесты через UI и генерировать команды.
  • Команды Cypress можно расширять, создавая свои custom commands, например:
Cypress.Commands.add('login', (username, password) => {
  cy.findByLabelText(/логин/i).type(username);
  cy.findByLabelText(/пароль/i).type(password);
  cy.findByRole('button', { name: /войти/i }).click();
});

Эти команды сокращают повторяющийся код и делают тесты более читаемыми.

Тщательное использование этих инструментов обеспечивает надёжное тестирование React компонентов на всех уровнях — от отдельных элементов до интеграционных сценариев с API и состоянием приложения.