Основные принципы React Native Testing Library
Если Jest — это ваш основной инструмент для тестирования логики и базовых функций, то React Native Testing Library — это про взаимодействие пользователя с интерфейсом. Эта библиотека помогает тестировать ваши компоненты так же, как это делал бы настоящий пользователь: ввод данных, нажатия кнопок, прокрутка списков и прочее.
React Native Testing Library следует принципу "тестировать так, как видит пользователь". Это значит:
- Мы имитируем поведение настоящего пользователя, фокусируясь на том, как компонент "выглядит" и "взаимодействует".
- Вместо поиска элементов по внутренним идентификаторам (id), мы ориентируемся на текст, метки или доступность (accessibilityLabel). Таким образом, тесты становятся более "человеко-ориентированными".
Почему это важно? Потому что, если интерфейс перестанет работать для пользователя, его не спасёт идеальная логика в вашем Redux или идеальная оптимизация рендеров. UI — это первое, что видят и с чем взаимодействуют пользователи.
Установка React Native Testing Library
Прежде чем начать проверку пользовательского интерфейса, нужно установить библиотеку.
Обычно в современных проектах уже настроен Jest, поэтому установка RNTL минимальна:
npm install --save-dev @testing-library/react-native
Кроме того, убедитесь, что у вас установлены следующие зависимости:
npm install --save-dev jest @types/jest react-test-renderer
Теперь всё готово! Время писать тесты.
Создание тестов для пользовательского интерфейса
Давайте создадим базовый тест, проверяющий, что наш компонент корректно рендерится. Допустим, у нас есть простое приложение с компонентом LoginScreen, который содержит:
- Поле ввода имени пользователя.
- Поле ввода пароля.
- Кнопку для входа.
Файл компонента LoginScreen.tsx:
import React from 'react';
import { View, Text, TextInput, Button } from 'react-native';
export const LoginScreen = () => {
return (
<View>
<Text>Логин</Text>
<TextInput placeholder="Введите имя пользователя" />
<TextInput placeholder="Введите пароль" secureTextEntry />
<Button title="Войти" onPress={() => {}} />
</View>
);
};
Теперь добавим тест, который проверяет, что все элементы отображаются корректно:
Файл LoginScreen.test.tsx:
import React from 'react';
import { render } from '@testing-library/react-native';
import { LoginScreen } from './LoginScreen';
test('LoginScreen рендерится правильно', () => {
const { getByText, getByPlaceholderText } = render(<LoginScreen />);
// Проверяем текстовые элементы
expect(getByText('Логин')).toBeTruthy();
// Проверяем поля ввода
expect(getByPlaceholderText('Введите имя пользователя')).toBeTruthy();
expect(getByPlaceholderText('Введите пароль')).toBeTruthy();
// Проверяем кнопку
expect(getByText('Войти')).toBeTruthy();
});
Этот тест:
- Использует функцию
renderот React Native Testing Library, чтобы отобразить компонент в тестовом окружении. - Проверяет наличие текста и плейсхолдеров через функции
getByTextиgetByPlaceholderText.
Тестирование взаимодействий
Теперь добавим обработчик для кнопки и протестируем взаимодействие. Изменим наш компонент:
import React, { useState } from 'react';
import { View, Text, TextInput, Button, Alert } from 'react-native';
export const LoginScreen = () => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const handleLogin = () => {
if (username === 'admin' && password === 'password') {
Alert.alert('Добро пожаловать!');
} else {
Alert.alert('Ошибка: Неверные данные');
}
};
return (
<View>
<Text>Логин</Text>
<TextInput
placeholder="Введите имя пользователя"
value={username}
onChangeText={text => setUsername(text)}
/>
<TextInput
placeholder="Введите пароль"
value={password}
secureTextEntry
onChangeText={text => setPassword(text)}
/>
<Button title="Войти" onPress={handleLogin} />
</View>
);
};
Теперь тестируем, что при вводе правильных данных отображается нужное сообщение:
Файл LoginScreen.test.tsx:
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import { LoginScreen } from './LoginScreen';
test('При вводе верных данных отображается сообщение об успешном входе', () => {
const { getByPlaceholderText, getByText } = render(<LoginScreen />);
// Заполняем поля ввода
fireEvent.changeText(getByPlaceholderText('Введите имя пользователя'), 'admin');
fireEvent.changeText(getByPlaceholderText('Введите пароль'), 'password');
// Нажимаем кнопку "Войти"
fireEvent.press(getByText('Войти'));
// Проверяем, что всплывает правильное сообщение
expect(Alert.alert).toHaveBeenCalledWith('Добро пожаловать!');
});
test('При вводе неверных данных отображается ошибка', () => {
const { getByPlaceholderText, getByText } = render(<LoginScreen />);
// Заполняем поля ввода
fireEvent.changeText(getByPlaceholderText('Введите имя пользователя'), 'wrongUser');
fireEvent.changeText(getByPlaceholderText('Введите пароль'), 'wrongPassword');
// Нажимаем кнопку "Войти"
fireEvent.press(getByText('Войти'));
// Проверяем, что всплывает сообщение об ошибке
expect(Alert.alert).toHaveBeenCalledWith('Ошибка: Неверные данные');
});
В этих тестах:
fireEvent.changeTextиспользуется для симуляции ввода текста в поля.fireEvent.pressсимулирует нажатие на кнопку.- Мы проверяем, чтобы
Alert.alertбыл вызван с правильным сообщением.
📝 Примечание: чтобы протестировать Alert.alert, нужно замокать его в тестах. В Jest это делается так:
jest.spyOn(global, 'Alert').mockImplementation(() => {});
Отладка и улучшение тестов
Писать тесты легко, пока всё идёт по плану, но могут, конечно, возникать трудности:
- Ваш тест может зависнуть, если элемент интерфейса не находится. Используйте
debug()от RNTL, чтобы увидеть текущий "рендер" компонента и понять, что происходит. - Если тесты выполняются медленно, проверьте, нет ли лишних рендеров компонентов.
- Разделяйте крупные тесты на несколько небольших, чтобы упростить отладку.
Пример использования debug():
const { debug } = render(<LoginScreen />);
debug(); // Покажет текущую структуру рендера, доступную для тестирования
Почему это важно для реальных проектов
Тестирование UI помогает в первую очередь избежать регрессий: когда вы изменяете функциональность, это часто ломает существующий интерфейс. Автоматические тесты интерфейса дают уверенность, что хотя бы базовые сценарии работы (логин, ввод данных, переход между экранами) остаются функциональными.
В реальных проектах React Native Testing Library часто используется для обеспечения качества пользовательского опыта.
Теперь у вас есть всё необходимое, чтобы начать тестировать пользовательский интерфейс вашего мобильного приложения с помощью React Native Testing Library. Удачи и помните: тесты — это не лишняя работа, это ваша страховка на будущее!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ