React Native тестирование

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

Установка и настройка

Для начала работы с React Testing Library в проекте React Native необходимо установить несколько зависимостей. Это включает саму библиотеку, а также адаптеры для работы с мобильным окружением.

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

После установки библиотеки следует настроить Jest, который будет выполнять тесты. Для этого можно использовать базовую конфигурацию Jest, которая совместима с React Native. Важно, чтобы Jest был настроен на использование мока для некоторых специфичных для мобильных платформ модулей.

Пример базовой настройки в jest.config.js:

module.exports = {
  preset: 'react-native',
  setupFiles: ['./jest/setup.js'],
  transformIgnorePatterns: [
    'node_modules/(?!@react-native|react-native|@react-navigation)'
  ],
};

Файл setup.js может включать дополнительные настройки для Jest и RTL.

Основные принципы работы с React Testing Library

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

Для тестирования компонента необходимо его отрендерить. В React Native это выполняется с помощью функции render из библиотеки @testing-library/react-native.

Пример:

import { render } from '@testing-library/react-native';
import React from 'react';
import MyComponent from '../MyComponent';

test('Компонент рендерится правильно', () => {
  const { getByText } = render(<MyComponent />);
  expect(getByText('Привет, мир!')).toBeTruthy();
});

Функция render возвращает набор методов для поиска элементов в компоненте, таких как getByText, getByTestId и другие.

Поиск элементов

React Testing Library предоставляет несколько способов для поиска элементов в рендере компонента:

  1. getByText — используется для поиска элемента по текстовому содержимому.
  2. getByTestId — поиск элемента по атрибуту testID, который может быть установлен для тестирования компонентов.
  3. getByPlaceholderText — поиск по тексту в плейсхолдере (подходит для инпутов и текстовых полей).
  4. getByLabelText — используется для поиска элементов, связанных с метками (<label>), что особенно полезно для формы.
  5. getByRole — поиск элементов по роли (например, кнопка, ссылка и т.д.).

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

test('Поиск элемента по testID', () => {
  const { getByTestId } = render(<MyComponent />);
  const button = getByTestId('submit-button');
  expect(button).toBeTruthy();
});

Симуляция событий

Для проверки взаимодействий с пользователем RTL предлагает API для симуляции событий. Например, чтобы нажать кнопку, можно использовать метод fireEvent.

Пример:

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

test('Клик по кнопке вызывает нужную функцию', () => {
  const mockFunction = jest.fn();
  const { getByTestId } = render(<MyComponent onPr ess={mockFunction} />);
  
  const button = getByTestId('submit-button');
  fireEvent.press(button);
  
  expect(mockFunction).toHaveBeenCalledTimes(1);
});

Метод fireEvent.press используется для симуляции нажатия на элементы, такие как кнопки или зоны на экране.

Асинхронные операции

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

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

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

test('Асинхронное обновление текста', async () => {
  render(<MyComponent />);
  
  // Ожидаем, что элемент с текстом будет найден через 500 мс
  const textElement = await screen.findByText('Данные загружены');
  expect(textElement).toBeTruthy();
});

Метод findByText является асинхронным и возвращает обещание, которое будет разрешено, когда элемент появится в DOM. waitFor используется для явного ожидания состояния компонента, пока не произойдут необходимые изменения.

Моки и заглушки

Иногда для тестирования компонентов необходимо использовать моки для внешних зависимостей или сервисов, таких как API-запросы. Это можно сделать с помощью Jest, создавая моки для функций или модулей, которые компонент использует.

Пример мока функции:

jest.mock('axios');

test('Загрузка данных с API', async () => {
  axios.get.mockResolvedValue({ data: { message: 'Данные загружены' } });
  
  const { getByText } = render(<MyComponent />);
  
  const textElement = await findByText('Данные загружены');
  expect(textElement).toBeTruthy();
});

В этом примере мокаем axios.get, чтобы он возвращал поддельные данные, что позволяет тестировать компонент, не выполняя реальные запросы.

Тестирование взаимодействий с пользователем

React Native включает множество компонентов, с которыми пользователь может взаимодействовать. Эти компоненты могут включать кнопки, текстовые поля, переключатели и другие элементы. Важно протестировать, как они реагируют на действия пользователя.

Пример тестирования поля ввода:

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

test('Изменение текста в поле ввода', () => {
  const { getByPlaceholderText } = render(<MyComponent />);
  
  const inputField = getByPlaceholderText('Введите текст');
  fireEvent.changeText(inputField, 'Тестовое значение');
  
  expect(inputField.props.value).toBe('Тестовое значение');
});

Здесь мы симулируем ввод текста в поле и проверяем, что значение поля обновилось.

Мокирование навигации

В мобильных приложениях часто используется навигация между экранами. Для тестирования компонентов с навигацией можно использовать моки из библиотеки @react-navigation/native и их взаимодействие с React Testing Library.

Пример использования мока навигации:

import { render } from '@testing-library/react-native';
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import MyComponent from '../MyComponent';

const Stack = createStackNavigator();

test('Навигация между экранами', () => {
  const { getByTestId } = render(
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={MyComponent} />
      </Stack.Navigator>
    </NavigationContainer>
  );
  
  const button = getByTestId('navigate-button');
  fireEvent.press(button);
  
  // Здесь можно добавить проверку, что навигация произошла
});

Здесь мы тестируем, что нажимая кнопку, происходит переход на другой экран.

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

  1. Минимизация использования моки: Постарайтесь минимизировать использование моков, чтобы тесты оставались приближенными к реальной работе приложения.
  2. Использование селекторов с доступностью: Используйте getByRole, getByLabelText и другие методы, которые делают ваш код более доступным и легко читаемым.
  3. Избегание тестирования внутренней реализации: Тесты должны проверять поведение компонента, а не его внутреннюю структуру. Это делает их более устойчивыми к изменениям.

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