--- name: testing description: Пиши тесты в андроид-проекте правильно. Используй этот навык при написании любых типов тестов (unit, integration, UI), тестировании бизнес-логики, сетевых функций и компонентов UI. --- # Тестирование ## 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-тестов: ```bash make test ``` Эта команда: 1. Запускает `./gradlew test --console=plain` - все unit-тесты (JVM, без устройства) 2. Автоматически выполняет скрипт `scripts/test_report.py` после завершения тестов 3. Показывает детальный отчет со статистикой по тестовым классам ### Скрипт test_report.py Скрипт `scripts/test_report.py` генерирует детальный отчет о результатах тестов: - Показывает общую статистику: всего тестов, успешные, упавшие - Выводит список всех упавших тестов с именами классов и методов - Отображает таблицу статистики по тестовым классам (всего, упало, успешно) - Сортирует классы по количеству упавших тестов (по убыванию) - Использует цвета для удобного чтения (зеленый для успеха, красный для ошибок) - Возвращает exit code 0 если все тесты прошли, 1 если есть упавшие Пример вывода скрипта: ``` ================================================================================ ✅ СБОРКА УСПЕШНА ================================================================================ Статистика тестов: Всего тестов: 145 ✅ Успешные: 142 ❌ Упавшие: 3 ❌ Список упавших тестов: - AuthViewModelTest::login_withInvalidCredentials_returnsError - EventsViewModelTest::loadEvents_whenNetworkError_returnsError - ParksRepositoryImplTest::getParks_whenEmpty_returnsEmptyList ================================================================================ Статистика по тестовым классам (5 классов): ================================================================================ Класс Упало Успешно Всего ---------------------------------------------------------------------------- AuthViewModelTest 1 12 13 EventsViewModelTest 1 8 9 ParksRepositoryImplTest 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 через unit-тесты с MockK в test/ - ⚠️ **Возможно (но не рекомендуется):** Интеграционные тесты ViewModels в androidTest/ с использованием `runTest`, `MainDispatcherRule` и `Turbine` - ✅ **Допустимо:** Интеграционные тесты для компонентов с Android API (например, CryptoManager с Android Keystore) - ✅ **Допустимо:** UI-тесты для Compose компонентов без бизнес-логики **Почему unit-тесты с MockK предпочтительнее:** - Быстрее выполняются (не нужны реальный Repository/БД) - Более стабильны и изолированы - В Jetpack-WorkoutApp все ViewModels тестируются через unit-тесты (AuthViewModelTest, EventsViewModelTest) **Когда использовать интеграционные тесты ViewModels:** - Только если нужно протестировать интеграцию с реальным Repository/БД - В JetpackWorkoutApp пока не используется, но технически возможно ### Рабочий подход к тестированию См. подробные примеры в [references/EXAMPLES.md](references/EXAMPLES.md). #### Unit-тесты ViewModels (с MockK) **Важно:** ViewModels, использующие `viewModelScope.launch`, требуют `MainDispatcherRule` в тестах. `viewModelScope` работает на `Dispatchers.Main`, который не настроен по умолчанию в Unit-тестах. Без `MainDispatcherRule` корутины не смогут запуститься, и проверки состояния будут неверными. **Пример создания `MainDispatcherRule`:** см. [references/EXAMPLES.md](references/EXAMPLES.md) -> "Создание MainDispatcherRule" #### Интеграционные тесты DAO и Repository #### Интеграционные тесты ViewModels (только для существующих) #### UI-тесты Compose компонентов ### Тестирование сетевых функций - ✅ **Только на моках**: Использовать MockK для API клиентов (SWApi, Retrofit) - ❌ **Без реальных запросов**: Запрещены реальные HTTP запросы к серверу в тестах - ✅ **Изолированное тестирование**: Тестировать бизнес-логику независимо от сети - ✅ **Предсказуемые ответы**: Использовать фиксированные mock-ответы от сервера См. примеры в [references/EXAMPLES.md](references/EXAMPLES.md): #### Пример тестирования с моком #### Мокирование HttpException ### Тестирование Flow с исключениями **Важно:** Для Flow с исключениями, которые обрабатываются через `catch`, используйте `first()` или `collect()` вместо Turbine. См. примеры в [references/EXAMPLES.md](references/EXAMPLES.md): #### Тестирование IOException (обрабатывается в catch) #### Тестирование других исключений (пробрасываются дальше) #### Мокирование Android Log ### Общие практики - Быстрые и независимые тесты - Описательные имена - Один тест - одна проверка - Тестировать поведение, не реализацию - Интеграционные тесты только для DAO и Repository - Unit-тесты для ViewModels с моками - Сетевые функции только на моках (без реальных запросов) - UI-тесты для Compose компонентов без бизнес-логики - Использовать JUnit 5 аннотации (`@Test`, `@Before`, `@After`) - Использовать assertions JUnit 5 (`assertEquals`, `assertTrue`, `assertNull`)