Когда использовать мокирование и когда не использовать
Что проверяют
Понимаете ли вы, что мок проверяет ваше представление о зависимости, а не саму зависимость, и умеете ли выбирать между моком, фейком и настоящей реализацией. Следом часто спрашивают, чем вы мокаете в Kotlin и как тестируете Room и Retrofit.
Как отвечать
Мокать стоит то, что медленно, нестабильно или неподконтрольно тесту:
- сеть: репозиторий тестируют с подменой
ApiService, а сам слой Retrofit лучше гонять черезMockWebServerс настоящим JSON, так проверяется и парсинг; - время и случайность: абстракция вроде
Clockили провайдера UUID, иначе тест не воспроизводится; - редкие ветки: ошибка сервера, пустой ответ, исключение, которое трудно вызвать по-настоящему;
- побочный эффект как результат: если смысл метода в том, чтобы отправить событие аналитики,
verifyв MockK или Mockito уместен.
Не мокать лучше то, что дёшево создать настоящим:
- модели и
data class: просто создайте объект; - свои use case и простые классы: берите реальные, иначе тест проверяет только связность моков;
- базу:
Room.inMemoryDatabaseBuilderпроверяет реальные SQL-запросы, мокDaoих не проверит; - Android SDK: мокать
Contextвручную хрупко, лучше вынести зависимость за интерфейс или взять Robolectric.
Документация Android советует предпочитать фейки: простая реализация интерфейса репозитория на MutableStateFlow читается лучше, чем двадцать строк every { ... } returns .... Для корутин нужен runTest, для проверки Flow удобна Turbine.
Где спотыкаются
Проверяют через verify каждый внутренний вызов. Тест ломается от любого рефакторинга, хотя поведение не изменилось.
Мокают типы, которыми не владеют, например Retrofit или SharedPreferences. Мок повторяет ваше ожидание от библиотеки, а не её реальное поведение.
Открывают классы ради тестов, потому что «Mockito не мокает final». В Mockito 5 inline mock maker включён по умолчанию, а MockK умеет это из коробки.