E2E-тестирование React Native: настройка Detox и CI
Вы запускаете CI, а тесты падают на ровном месте — то анимация не дождалась, то элемент не найден. Знакомо? Один из наших клиентов — FinTech-приложение с 50 экранами — страдал от 30% flaky-падений на каждую сборку. Разработчики тратили часы на перезапуски, а релизы задерживались на недели. После внедрения Detox мы снизили процент падений до 5% и сократили время прогона в 3 раза за счёт параллельного запуска. Detox — gray-box фреймворк, который встраивает тест-сервер прямо в процесс приложения. В отличие от Appium, Detox автоматически синхронизируется с event loop React Native, что даёт стабильные тесты без sleep(). Это особенно важно для проектов с динамическими анимациями и частыми сетевыми запросами. Наш опыт — 5+ лет и более 15 реализованных проектов — позволяет гарантировать стабильность тестов даже на сложных UI.
Почему Detox, а не Appium?
| Критерий |
Detox |
Appium |
| Тип доступа |
Gray-box (встроенный сервер) |
Black-box (внешний) |
| Синхронизация |
Автоматическая с JS-потоком |
Требует sleep() и ожиданий |
| Стабильность на RN |
Высокая |
Низкая (часто падают) |
| Поддержка анимаций |
Встроенная |
Через ожидания |
| Скорость выполнения |
Быстрее |
Медленнее из-за задержек |
| Процент flaky на CI |
5-10% |
30-50% |
Мы используем Detox, потому что это единственный фреймворк, который понимает внутреннюю работу React Native. Результат — тесты, которые не падают на ровном месте в CI.
Как мы конфигурируем Detox: первые грабли
Настройка Detox занимает больше времени, чем кажется. Особенно на iOS. Вот конфигурация, которую мы используем по умолчанию:
{
"detox": {
"testRunner": {
"args": { "$0": "jest", "config": "e2e/jest.config.js" },
"jest": { "setupTimeout": 120000 }
},
"apps": {
"ios.debug": {
"type": "ios.app",
"binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/MyApp.app",
"build": "xcodebuild -workspace ios/MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build"
},
"android.debug": {
"type": "android.apk",
"binaryPath": "android/app/build/outputs/apk/debug/app-debug.apk",
"build": "cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug"
}
},
"devices": {
"simulator": {
"type": "ios.simulator",
"device": { "type": "iPhone 15", "os": "iOS 17.4" }
},
"emulator": {
"type": "android.emulator",
"device": { "avdName": "Pixel_7_API_34" }
}
}
}
}
Частая проблема: APK должен быть собран с assembleAndroidTest — иначе синхронизация не работает. На iOS — только симулятор билд (-sdk iphonesimulator). Реальные устройства требуют отдельного профилирования и signing. Также важно настроить setupTimeout — для больших проектов мы ставим 120 секунд, чтобы тесты успели инициализироваться.
Как избежать flaky-тестов?
Flaky-тесты — главная боль CI. Detox снижает их количество, но полностью не исключает. Вот таблица с типичными причинами и решениями:
| Причина |
Решение |
| Анимации зациклены |
Отключить в test build через флаг detoxDisableHierarchyDump или кастомный IS_TESTING |
| Сетевые запросы не завершены |
Использовать waitFor с таймаутом, или перехватывать запросы через mock |
| Состояние экрана не сброшено |
Вызывать device.reloadReactNative() перед каждым тестом |
| Таймауты Jest недостаточны |
Увеличить jest.setTimeout до 120 секунд |
| Разные версии iOS/Android |
Тестировать на тех же версиях, что и в CI |
Также используем соглашение по testID: screen_component_action. Это упрощает поддержку и поиск элементов.
Параллельное тестирование: как ускорить CI?
Detox поддерживает параллельный запуск через шардирование Jest. Команда:
detox test --configuration android.debug --workers 3
Требует 3 эмулятора или симулятора. На macOS нужно 16 ГБ RAM для 3 симуляторов. AVD создаются автоматически при наличии прав. Параллельный запуск сокращает время полного прогона с 45 минут до 15 минут — в 3 раза быстрее.
Интеграция в CI: рецепт для GitHub Actions
Для iOS используем macOS-раннер, для Android — ubuntu вместе с reactivecircus/android-emulator-runner. Пример сборки и тестов:
jobs:
e2e-ios:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm ci
- run: npx pod-install
- run: npx detox build --configuration ios.debug
- run: npx detox test --configuration ios.debug --headless
Для Android — обязательный --headless, иначе эмулятор не найдёт дисплей. Также стоит кэшировать симуляторы и эмуляторы, чтобы не создавать их заново.
Типичные команды Detox для отладки
-
detox test —reuse — переиспользовать уже запущенное устройство.
-
detox test —record-logs all — записать логи всех тестов.
-
detox build —configuration ios.debug 2>&1 | tee build.log — сохранить лог сборки.
-
detox test —debug — запустить с дебаг-режимом.
Что входит в работу
- Полная конфигурация Detox под iOS и Android
- Написание тестов для ключевых пользовательских флоу (авторизация, навигация, платежи)
- Документация по настройке и расширению тестов
- Интеграция в ваш CI/CD (GitHub Actions, GitLab CI, Jenkins)
- Обучение команды (2 часа + доступ к записи)
- Гарантия стабильности тестов: не более 5% flaky-падений
Сроки и стоимость
Базовая настройка конфигурации и покрытие основных флоу занимает 5 дней. При наличии сложных нативных модулей (push-уведомления, биометрия, камера) — до 7 дней. Стоимость рассчитывается индивидуально — напишите нам, и мы проанализируем ваше приложение и предложим оптимальный план.
Мы гарантируем стабильность тестов на всех этапах. Обращайтесь — поможем вашей команде забыть о flaky-тестах и ускорить релизный цикл. Получите консультацию: отправьте заявку, и мы свяжемся с вами в течение дня.
Тестирование мобильных приложений: XCTest, Espresso, Detox и Appium
Flaky-тест, падающий на CI раз в пять запусков без воспроизводимой причины, хуже его отсутствия. Команда перестаёт доверять инфраструктуре и отключает тесты — регрессии проскакивают в продакшн. Мы это видим каждый день и знаем, как выстроить надёжную систему тестирования, которая не требует постоянного внимания. Получите консультацию — оценим ваш проект и предложим архитектуру тестов под ваш стек.
Почему flaky-тесты опасны?
Одна нестабильная проверка может завалить пайплайн, заблокировав релиз. Разработчики тратят 15-20% рабочего времени на перезапуск и анализ ложнонегативных сбоев. Автоматизация без стабильности — не экономия, а потеря эффективности. Мы решаем эту проблему на уровне архитектуры: Gray Box-фреймворки (Detox, Patrol) синхронизируются с состоянием приложения, а native инструменты (XCUITest, Espresso) получают правильные IdlingResource и accessibilityIdentifier. Результат: стабильность >99% на CI.
Unit-тесты: что тестировать, а что нет
На iOS XCTest — основа. Бизнес-логика в ViewModel, Interactor, UseCase — тестируется без проблем, если она не тянет UIKit. Типичная ошибка: логика в UIViewController напрямую — тогда unit-тест требует создания view-иерархии, что медленно и нестабильно. Выход — выносить логику в сервисы с @testable import.
Для асинхронного кода в Swift: XCTestExpectation для старого стиля, await + XCTest async для современного. С Combine — XCTestExpectation + sink, но удобнее использовать библиотеки типа CombineExpectations. На Android JUnit 4/5 + Mockito для unit-тестов, Coroutines Test для suspend-функций. runTest {} из kotlinx-coroutines-test — стандарт для ViewModel с StateFlow. Покрытие кода unit-тестами на уровне 80% сокращает время регрессии на 60% (данные наших проектов).
UI-тесты: стабильность важнее покрытия
XCUITest (iOS) и Espresso (Android) — нативные UI-тесты. Работают быстро, интегрированы с IDE, но тестируют одну платформу. Главная проблема XCUITest — хрупкость селекторов. app.buttons["Войти"] падает при смене локализации или рефакторинге accessibility label. Правильный подход: accessibilityIdentifier для тестируемых элементов, никогда не текстовые метки. Идентификаторы из shared enum — чтобы они не расходились между приложением и тестами. Опыт показывает: такая практика снижает flakiness на 90%.
Espresso на Android стабильнее из-за IdlingResource механизма — тест автоматически ждёт завершения background операций. Но кастомные async операции (OkHttp, кастомные Executors) нужно регистрировать в IdlingRegistry вручную, иначе тест не синхронизируется с сетевыми запросами. Мы гарантируем правильную настройку IdlingResource на этапе аудита.
Detox и Patrol: end-to-end для React Native и Flutter
Detox — E2E фреймворк для React Native, разработанный Wix. Работает на реальных устройствах и симуляторах через Gray Box подход: знает о состоянии JS thread и синхронизируется с ним. Это решает главный flakiness-источник — тест не нажимает кнопку, пока приложение занято. Настройка Detox нетривиальна. Требует специальный debug-билд с DetoxInstrumentsServer, конфигурации в package.json и отдельного Appium-сервера не нужно. Типичная проблема: тест стабилен на симуляторе, падает на реальном устройстве из-за анимаций. Решение — animations: disabled в Detox конфигурации для E2E билда.
Patrol — аналог для Flutter. Расширяет встроенный integration_test пакет и добавляет возможность взаимодействовать с нативными системными диалогами (permission prompts, notifications) — то, что flutter_driver и базовый integration_test не умеют. Для CI используется через patrol test --target integration_test/app_test.dart.
Appium: кроссплатформа с ценой
Appium — когда нужно покрыть iOS и Android одними тестами. Использует WebDriver протокол, поверх XCUITest и UiAutomator2 драйверов. Скорость ниже нативных фреймворков, но для команд без ресурсов на две тестовые кодовые базы — компромисс. Appium 2.x с плагинной архитектурой заметно удобнее первой версии. appium-doctor диагностирует окружение — полезен при настройке CI.
CI и параллелизация
Для параллельного запуска XCUITest используем Xcode Cloud или xcodebuild test-without-building с несколькими симуляторами через parallel-testing-enabled. Время прогона 200 UI-тестов с параллелизацией на 4 симулятора — с 40 минут до 12. На Android аналогично используем Firebase Test Lab с шардингом.
| Фреймворк |
Платформа |
Gray Box |
Скорость |
Системные диалоги |
| XCUITest |
iOS |
Нет |
Высокая |
Да (через addUIInterruptionMonitor) |
| Espresso |
Android |
Да (IdlingResource) |
Высокая |
Ограничено |
| Detox |
React Native |
Да |
Средняя |
Ограничено |
| Patrol |
Flutter |
Частично |
Средняя |
Да |
| Appium |
iOS + Android |
Нет |
Низкая |
Да |
Типичные ошибки при настройке (и как их избежать)
| Ошибка |
Последствие |
Решение |
| Использование текстовых меток в селекторах |
Тесты падают при локализации |
accessibilityIdentifier из enum |
| Отсутствие IdlingResource для кастомных Executor |
Espresso не ждёт ответа сервера |
Регистрация в IdlingRegistry |
| Включённые анимации на реальном устройстве в Detox |
Flaky тесты из-за таймингов |
animations: disabled в E2E билде |
| Параллелизация без изоляции состояния |
Гонки данных между тестами |
Запуск каждого теста в свежем симуляторе |
Как мы это делаем: процесс работы
-
Аудит текущего кода и CI — оцениваем flakiness, покрытие, узкие места.
-
Проектирование тестовой архитектуры — выбираем фреймворк, селекторы, моки.
-
Настройка инфраструктуры — CI пайплайн, parallel execution, отчёты (Allure, Xcode Report).
-
Написание тестов — unit, UI, E2E, performance (XCTMetrics, Macrobenchmark).
-
Интеграция и стабилизация — прогон 200+ тестов, отлов flaky-кейсов.
-
Передача документации — архитектура, запуск, troubleshooting.
Что входит в работу (deliverables)
- Архитектурная документация тестового покрытия
- Настроенный CI-пайплайн с параллелизацией и отчётами
- Код тестов (unit, UI, E2E) с styleguide
- Обучение команды (2 часа воркшопа)
- Доступ к тестовым билдам и CI-логам
- Поддержка в течение месяца после сдачи (фикс flakiness, обновление под новые версии)
Сроки ориентировочно
Настройка инфраструктуры с нуля (CI, unit + UI тесты, отчёты) — 2-3 недели. Написание покрытия для существующего приложения — от 2 недель до месяца в зависимости от объёма. Оценим ваш проект за 2 дня — свяжитесь с нами. 5+ лет опыта в автоматизации, 50+ успешных проектов, сертифицированные специалисты по iOS/Android. Гарантируем стабильность тестов >98% на CI после внедрения.