Какие подходы к тестированию компонентов вы используете?

Тестирование компонентов в React Native (как и в React) — важная часть обеспечения качества приложения. Цель тестирования — убедиться, что компоненты работают корректно в разных условиях, корректно взаимодействуют с пользователем, обрабатывают состояния и правильно реагируют на события. Существует несколько подходов к тестированию компонентов, включая unit-тесты, snapshot-тесты, интеграционные и e2e (end-to-end) тесты. Каждый из них решает разные задачи и применяется в определённом контексте.

Unit-тестирование компонентов

Unit-тесты (модульные) проверяют логику конкретного компонента или функции изолированно от внешних зависимостей. Используются такие инструменты, как:

  • Jest — фреймворк для юнит-тестирования, входит по умолчанию в React Native

  • @testing-library/react-native — библиотека для взаимодействия с компонентами через пользовательский интерфейс (аналог Testing Library для веба)

Пример:

import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import MyButton from '../MyButton';
test('кнопка вызывает обработчик при нажатии', () => {
const onPressMock = jest.fn();
const { getByText } = render(<MyButton onPress={onPressMock} title="Жми" />);
fireEvent.press(getByText('Жми'));
expect(onPressMock).toHaveBeenCalledTimes(1);
});

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

  • Быстро выполняются

  • Не зависят от платформы

  • Можно покрыть большое количество логики

Snapshot-тестирование

Снимки (snapshots) позволяют сохранять структуру отрендеренного компонента и сравнивать её при каждом тестировании. Используется Jest с методом toMatchSnapshot.

import React from 'react';
import renderer from 'react-test-renderer';
import MyComponent from '../MyComponent';
test('рендеринг компонента совпадает со снимком', () => {
const tree = renderer.create(<MyComponent />).toJSON();
expect(tree).toMatchSnapshot();
});

Когда структура JSX изменится, тест не пройдёт, и разработчику нужно вручную подтвердить или отклонить изменения.

Недостатки:

  • Снимки легко «одобряются» без анализа

  • Не ловят функциональные ошибки, только структуру

Интеграционные тесты

Проверяют, как несколько компонентов взаимодействуют между собой: передача props, взаимодействие, совместная работа. Используются те же инструменты: Jest и Testing Library.

test('форма передаёт данные в родительский компонент', () => {
const onSubmitMock = jest.fn();
const { getByPlaceholderText, getByText } = render(
<MyForm onSubmit={onSubmitMock} />
);
fireEvent.changeText(getByPlaceholderText('Введите имя'), 'Иван');
fireEvent.press(getByText('Отправить'));
expect(onSubmitMock).toHaveBeenCalledWith('Иван');
});

Цель — проверить, как компоненты передают данные и реагируют на действия пользователя.

End-to-End (E2E) тестирование

E2E-тесты проверяют поведение всего приложения «глазами пользователя»: навигация, формы, списки, сетевые ошибки. Используются инструменты:

  • Detox — E2E-фреймворк для React Native

  • Appium, Maestro (альтернативы)

Установка Detox:

npm install detox --save-dev
npx detox init -r jest

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

describe('Пример E2E теста', () => {
beforeAll(async () => {
await device.launchApp();
});
it('показывает главный экран', async () => {
await expect(element(by.id('home-screen'))).toBeVisible();
});
it('навигация на экран настроек', async () => {
await element(by.id('settings-button')).tap();
await expect(element(by.id('settings-screen'))).toBeVisible();
});
});

Detox выполняет тесты на реальном или эмулированном устройстве, что позволяет проверить производительность, задержки, работу с API и нативными модулями.

Тестирование хуков и логики

Для функциональных компонентов важно отдельно тестировать бизнес-логику и хуки (useState, useEffect, useContext, кастомные хуки).

Существуют библиотеки вроде @testing-library/react-hooks, но теперь они считаются устаревшими. Сейчас обычно оборачивают хук в тестовый компонент:

function TestComponent() {
const value = useCustomHook();
return <Text>{value}</Text>;
}

Затем используют @testing-library/react-native для тестирования результата.

Mock API и внешние зависимости

Часто приходится мокать сетевые запросы, асинхронные хранилища и другие модули:

jest.mock('axios');

import axios from 'axios';
test('компонент отображает данные', async () => {
axios.get.mockResolvedValueOnce({ data: { name: 'Иван' } });
const { getByText, findByText } = render(<UserComponent />);
expect(await findByText('Иван')).toBeTruthy();
});

Для моков модулей, таких как AsyncStorage:

jest.mock('@react-native-async-storage/async-storage', () =>
require('@react-native-async-storage/async-storage/jest/async-storage-mock')
);

Покрытие кода (Coverage)

Jest позволяет измерять, какая часть кода покрыта тестами:

npx jest --coverage

Показывает статистику: функции, строки, ветвления. Цель — не только добиться % покрытия, но и тестировать важную логику.

CI-интеграция тестов

Тесты должны запускаться автоматически при каждом коммите или pull request:

  • GitHub Actions

  • Bitrise

  • CircleCI

Для Detox можно запускать тесты на эмуляторе/симуляторе в CI среде.

Подходы и стратегии

  1. TDD (Test-Driven Development) — сначала пишется тест, потом код

  2. BDD (Behavior-Driven Development) — описываются сценарии поведения (в стиле describe, it)

  3. Unit + Integration + E2E — комбинированный подход: дешёвые быстрые тесты на логику, более редкие E2E на критические пути

  4. Компонентное тестирование UI — важно для сложных форм, таблиц, списков

Тестирование компонентов в React Native требует комбинации нескольких подходов: юнит-тестов для логики, интеграционных — для связи компонентов, e2e — для имитации поведения пользователя. Выбор зависит от масштаба проекта, целей команды и уровня критичности приложения.