Тестирование
When to Use
- •Используй этот навык, когда пишешь unit-тесты для бизнес-логики (ViewModels, Use Cases, Domain models)
- •Используй этот навык, когда пишешь интеграционные тесты для DAO и Repository
- •Используй этот навык, когда пишешь UI тесты для Compose компонентов
- •Используй этот навык, когда тестируешь Flow/StateFlow с использованием Turbine
- •Используй этот навык, когда нужно протестировать обработку исключений в корутинах
- •Этот навык полезен при выборе правильного типа теста для конкретного сценария
- •Этот навык помогает определить, когда использовать unit-тесты с моками, а когда интеграционные тесты
Типы тестов
- •Unit: бизнес-логика изолированно, MockK для зависимостей, AAA паттерн
- •Integration: взаимодействие слоев (DAO, Repository), реальные реализации БД
- •UI: критические сценарии, Compose Testing для компонентов
Инструменты
- •JUnit 5 - unit-тесты
- •MockK - мокирование
- •Compose Testing - Compose компоненты
- •Room Testing - для интеграционных тестов БД
- •kotlinx-coroutines-test - для тестирования корутин
- •Turbine - для тестирования Flow/StateFlow (app.cash.turbine:turbine:1.1.0)
Запуск тестов и отчеты
Команда make test
Используйте команду make test для запуска всех unit-тестов:
make test
Эта команда:
- •Запускает
./gradlew test --console=plain- все unit-тесты (JVM, без устройства) - •Автоматически выполняет скрипт
scripts/test_report.pyпосле завершения тестов - •Показывает детальный отчет со статистикой по тестовым классам
Скрипт test_report.py
Скрипт scripts/test_report.py генерирует детальный отчет о результатах тестов:
- •Показывает общую статистику: всего тестов, успешные, упавшие
- •Выводит список всех упавших тестов с именами классов и методов
- •Отображает таблицу статистики по тестовым классам (всего, упало, успешно)
- •Сортирует классы по количеству упавших тестов (по убыванию)
- •Использует цвета для удобного чтения (зеленый для успеха, красный для ошибок)
- •Возвращает exit code 0 если все тесты прошли, 1 если есть упавшие
Пример вывода скрипта:
================================================================================ ✅ СБОРКА УСПЕШНА ================================================================================ Статистика тестов: Всего тестов: 145 ✅ Успешные: 142 ❌ Упавшие: 3 ❌ Список упавших тестов: - MainScreenViewModelTest::addItem_withValidData_addsItem - DetailScreenViewModelTest::calculateDays_whenSameDate_returnsZero - ItemRepositoryImplTest::getAllItems_whenEmpty_returnsEmptyList ================================================================================ Статистика по тестовым классам (5 классов): ================================================================================ Класс Упало Успешно Всего ---------------------------------------------------------------------------- MainScreenViewModelTest 1 12 13 DetailScreenViewModelTest 1 8 9 ItemRepositoryImplTest 1 15 16 ================================================================================
Другие команды тестирования
- •
make android-test- запуск интеграционных тестов на Android устройстве - •
make test-all- запуск всех тестов (unit + интеграционные) - •
make android-test-report- открыть HTML отчет интеграционных тестов в браузере
Структура
- •
app/src/test/- unit-тесты (ViewModels, Use Cases, Domain models) - •
app/src/androidTest/- integration/UI тесты (DAO, Repository, UI компоненты) - •Структура зеркалит код
- •Имена классов:
*Test
Best Practices
Важно: Ограничения на интеграционные тесты с ViewModels
- •❌ Запрещено: Создавать новые интеграционные тесты с ViewModels
- •⚠️ Допустимо: Для существующих интеграционных тестов ViewModels использовать
runTest,MainDispatcherRuleиTurbine - •✅ Рекомендуется: Тестировать ViewModels только через unit-тесты с MockK
- •✅ Допустимо: Тестировать DAO и Repository через интеграционные тесты без ViewModels
- •✅ Допустимо: UI-тесты для Compose компонентов без бизнес-логики
Причина ограничений:
- •Конфликт между
runBlockingиviewModelScope.launch - •Flow репозитория не активируется корректно в тестах
- •Тесты зависают бесконечно или падают
- •Unit-тесты с MockK обеспечивают лучшее покрытие бизнес-логики
- •В JetpackDays часть интеграционных тестов ViewModels была отключена (@Ignore) из-за сложностей с асинхронностью
Отличия от Jetpack-WorkoutApp:
В Jetpack-WorkoutApp интеграционные тесты ViewModels возможны (но не рекомендуются), так как там используется сетевой слой с API. В JetpackDays работает полностью офлайн без сетевых запросов, поэтому unit-тесты с моками полностью покрывают бизнес-логику и нет необходимости в сложных интеграционных тестах ViewModels.
Рабочий подход к тестированию
См. подробные примеры в references/EXAMPLES.md.
Unit-тесты ViewModels (с MockK)
Интеграционные тесты DAO и Repository
Интеграционные тесты ViewModels (только для существующих)
UI-тесты Compose компонентов
Тестирование Flow с исключениями
Важно: Для Flow с исключениями, которые обрабатываются через catch, используйте first() или collect() вместо Turbine.
См. примеры в references/EXAMPLES.md:
Тестирование IOException (обрабатывается в catch)
Тестирование других исключений (пробрасываются дальше)
Мокирование Android Log
Общие практики
- •Быстрые и независимые тесты
- •Описательные имена
- •Один тест - одна проверка
- •Тестировать поведение, не реализацию
- •Интеграционные тесты только для DAO и Repository
- •Unit-тесты для ViewModels с моками
- •UI-тесты для Compose компонентов без бизнес-логики
- •Использовать JUnit 5 аннотации (
@Test,@Before,@After) - •Использовать assertions JUnit 5 (
assertEquals,assertTrue,assertNull)