Стратегия тестирования
Стратегия тестирования определяет, что и на каком уровне проверять, а также как сохранить обратную связь быстрой и надёжной. Цель - уверенность в важном поведении системы, а не заданный процент покрытия или максимально большой набор тестов.
Приоритеты тестирования
Что тестировать в первую очередь?
Расставляйте приоритеты по последствиям и вероятности ошибки. Начинайте с поведения, которое дорого сломать или трудно проверить вручную:
- бизнес-правила, вычисления и переходы состояния;
- обработка ошибок, повторные попытки и граничные условия;
- преобразования между сетевыми, хранимыми, доменными и UI-моделями;
- хранение данных, миграции, кеширование и offline-поведение;
- авторизация, платежи и другие критические пользовательские сценарии;
- regression-тесты для ошибок, которые уже дошли до пользователей.
Проверяйте наблюдаемое поведение через публичный API. Тесты, привязанные к private-методам или точной последовательности внутренних вызовов, часто ломаются при безопасном рефакторинге, не обнаруживая реальную регрессию.
Покрытие - диагностический сигнал, а не самостоятельная цель. Полностью покрытый mapper с низким риском может быть менее важен, чем один отсутствующий тест разрушительной миграции или перехода состояния платежа.
Размер теста и среда выполнения
Размер теста и место его запуска - разные решения:
| Масштаб | Назначение | Типичные Android-примеры |
|---|---|---|
| Малый / unit | Один изолированный модуль поведения | Mapper, validator, reducer, use case, ViewModel |
| Средний / integration | Несколько реальных участников | Repository с базой данных, сериализация, navigation contract |
| Большой / end-to-end | Пользовательский сценарий через несколько слоёв | Вход, оформление заказа, восстановление после offline |
Local tests выполняются на JVM хоста и обычно работают быстро. Instrumented tests запускаются на устройстве или эмуляторе и используют настоящий Android framework. При этом local test может проверять несколько модулей через Robolectric, а instrumented test - узкую интеграцию с framework, например миграцию Room.
Выбирайте самую дешёвую среду, которая достоверно проверяет поведение. Чистую Kotlin-логику оставляйте в local tests; используйте Robolectric, когда достаточно Android-поведения на хосте; переходите к instrumented tests, когда результат зависит от настоящего framework, устройства, реализации базы данных или rendering.
Test pyramid - модель стоимости, а не квота: много быстрых тестов для детерминированной логики, меньше integration tests для границ вроде Room, сериализации и cache policy repository, а также небольшое количество UI/end-to-end tests для критических сценариев. Не повторяйте через UI все комбинации бизнес-правил, если тесты нижнего уровня дают ту же уверенность.
Test doubles
Test double заменяет зависимость:
- fake - упрощённая рабочая реализация, например in-memory repository;
- stub возвращает заранее заданные ответы;
- mock записывает взаимодействия и проверяет вызовы.
Предпочитайте fakes или stubs, когда важен результат или состояние. Используйте mocks, когда требованием является само взаимодействие, например отправка одного analytics event. Если замокать всех участников, тест начинает повторять детали реализации и может скрыть проблемы интеграции.
Надёжность набора тестов
Полезный тест детерминирован, изолирован, читаем и достаточно быстр для своего цикла обратной связи. Контролируйте время, dispatchers, случайность и внешний I/O. Не используйте произвольные задержки: ожидайте наблюдаемое условие или применяйте виртуальное время. Сбрасывайте базы данных, DI-контейнеры и глобальное состояние между тестами.
Запускайте быстрые тесты при каждом изменении, а широкие наборы - в CI. Относитесь к flaky tests как к дефектам: исправляйте их или временно изолируйте с владельцем и сроком, а не привыкайте к повторным запускам.