Философия тестирования в Compose. В отличие от View-системы, где тесты часто симулируют пользовательский ввод и проверяют состояние виджетов, Compose поощряет тестирование через состояние (state) и события (events). Ваш UI — это функция состояния (`@Composable fun UI(state: State)`). Поэтому основная цель тестов — проверить, что для данного состояния отрисовывается правильный UI, и что пользовательские события правильно изменяют это состояние. Это разделение упрощает тестирование: логику состояния можно проверить юнит-тестами, а UI — инструментами Compose.
Инструментарий: JUnit, Compose Testing, и Robolectric. Основой являются `androidx.compose.ui:ui-test-junit4` и `ui-test-manifest`. Для запуска тестов на JVM без эмулятора или устройства используется Robolectric. Это значительно ускоряет разработку. Настройте проект, добавив необходимые зависимости в `build.gradle` модуля. Для тестов, требующих реального устройства (интеграционные), используется `androidx.compose.ui:ui-test`.
Тип 1: Тестирование отдельных компонентов (Composable) — Unit-тесты UI. Это аналог unit-тестов для ваших @Composable функций. Используйте правило `createComposeRule()` или `createAndroidComposeRule()`. Основные методы:
- `onNodeWithText`, `onNodeWithTag`, `onNodeWithContentDescription` — для поиска элементов в дереве.
- `assertIsDisplayed()`, `assertTextEquals()` — для проверок.
- `performClick()`, `performTextInput()` — для симуляции действий.
Тип 2: Тестирование состояния и взаимодействий — Интеграционные тесты. Здесь тестируется целый экран или большой фрагмент UI. Важно управлять зависимостями. Используйте паттерны Dependency Injection (например, Hilt) для подмены репозиториев или ViewModel на тестовые doubles (fake-объекты). Вы можете проверить полный сценарий: ввод текста в поле, нажатие кнопки, проверка появления прогресс-бара, затем успешного результата. Для асинхронных операций используйте `advanceTimeBy` и `waitForIdle` или более современные методы `awaitIdle`.
Тип 3: Скриншот-тестирование (Snapshot Testing). Это мощный метод регрессионного тестирования UI. Вы сохраняете «снимок» корректно выглядящего компонента, а при последующих запусках тест сравнивает текущий рендер с сохраненным эталоном. В экосистеме Compose для этого есть библиотека `com.google.accompanist:accompanist-testharness` или кастомные решения на основе `recordSnapshotAsBitmap()`. Важно запускать такие тесты на устройствах с одинаковой конфигурацией (плотность экрана, размер) или использовать правила нормализации. Snapshot-тесты отлично подходят для дизайн-систем и библиотек компонентов.
Советы и лучшие практики. 1. Избегайте тестов, зависящих от времени. Используйте тестовые корутин-диспетчеры (`TestCoroutineDispatcher`). 2. Декомпозируйте большие Composable-функции. Чем меньше ответственность у компонента, тем проще его протестировать. 3. Тестируйте логику в ViewModel отдельно, как обычные юнит-тесты. Compose UI-тесты должны проверять только связь между состоянием из ViewModel и его отображением. 4. Используйте `createComposeRule` для pure-JVM тестов с Robolectric и `createAndroidComposeRule` для тестов, требующих активности. 5. Для проверки сложных деревьев используйте `printToLog()` для отладки семантического дерева. 6. Организуйте тесты также, как и основной код: `ui/login/LoginScreenTest.kt`.
Распространенные ошибки и их решение. Ошибка: «Периодические падения тестов из-за анимаций или асинхронности». Решение: Отключайте анимации в тестовом режиме (`LocalAnimationClock provides ManualAnimationClock(0)`). Используйте `mainClock.advanceTimeByFrame()` для управления временем. Ошибка: «Тест не находит элемент». Решение: Убедитесь, что элемент действительно отрисован в текущем состоянии. Используйте `onRoot().printToLog()` для отладки. Возможно, нужно использовать `onNodeWithTag(useUnmergedTree = true)`. Ошибка: «Медленные тесты». Решение: Максимально переносите тесты на JVM с Robolectric. Оставляйте на реальном устройстве только критически важные интеграционные тесты.
Интеграция с CI/CD. Настройте pipeline для запуска UI-тестов. Для этого можно использовать Firebase Test Lab или эмуляторы в GitHub Actions/GitLab CI. Убедитесь, что снимки для snapshot-тестов делаются и сравниваются в стабильном окружении. Установите политику: падение UI-тестов блокирует мердж в основную ветку.
Тестирование в Jetpack Compose — это не страшно, а логично и мощно. Переход от императивного тестирования View к декларативной проверке состояния открывает новые возможности для создания стабильных и поддерживаемых тестов. Начиная с изолированных unit-тестов компонентов и заканчивая скриншот-тестированием всей дизайн-системы, вы можете построить надежный щит от регрессий для своего современного Android-приложения.
Комментарии (10)