Unit-тестирование Android-приложений: что это и как провести?

22
7 мин.

Unit-тестирование – один из ключевых инструментов качества в Android-разработке. По данным MoldStud (2025), команды, применяющие моки для изоляции зависимостей, запускают тесты до 70% быстрее по сравнению с тестами, обращающимися к реальным сетевым слоям. При этом 30% снижение дефектов фиксируется у команд, интегрировавших тесты в CI/CD-пайплайн. В этой статье – практическое руководство: от базовых понятий до написания первого теста с MockK и JUnit.

Что такое unit-тестирование в Android?

Юнит тестирование это проверка отдельной единицы кода – одной функции, метода или класса – в полной изоляции от остальной системы. В Android такой «единицей» чаще всего выступает ViewModel, UseCase или Repository. Unit-тест не запускает эмулятор, не обращается к базе данных и не делает сетевых запросов – он работает только с логикой конкретного класса, а все зависимости заменяются мок-объектами.

Тип тестаЧто проверяетГде запускаетсяСкорость
Unit-тестЛогику одного класса/методаJVM (без Android)Миллисекунды
ИнтеграционныйВзаимодействие нескольких компонентовJVM / эмуляторСекунды
UI (Espresso)Поведение интерфейса с точки зрения пользователяЭмулятор / устройствоСекунды-минуты

Пример: у вас есть LoginViewModel, который принимает email и пароль, валидирует их и вызывает AuthRepository. Unit-тест проверяет только логику валидации – без реального репозитория и без UI. AuthRepository заменяется моком, который возвращает нужный результат.

Зачем нужны unit-тесты в Android-разработке?

Зачем нужны unit тесты – вопрос, который задают в начале проекта и перестают задавать после первого крупного рефакторинга без покрытия. Вот пять конкретных причин:

  1. Быстрый фидбек. Тест на JVM выполняется за миллисекунды – в отличие от сборки и запуска на эмуляторе. Разработчик получает сигнал о баге сразу после изменения кода, а не через 2-3 минуты сборки.
  2. Безопасный рефакторинг. Если все unit-тесты проходят после изменений – поведение системы не нарушено. Без тестов рефакторинг превращается в угадывание: что сломалось, а что нет.
  3. Документация через код. Хорошо написанный тест – это спецификация поведения класса. Новый разработчик читает тест и понимает, что должен делать метод, без необходимости читать всю бизнес-логику. Это особенно ценно в больших Android-проектах, где правильная архитектура интерфейса закладывается с самого начала – подробнее об этом в материале «Интерфейс мобильных приложений: от А до Я».
  4. Снижение стоимости багов. Баг, найденный на этапе юнит-теста, дешевле в исправлении в 5-10 раз по сравнению с багом, найденным в продакшене (по данным IBM NIST Study, классический показатель стоимости дефекта по фазе).
  5. CI/CD-интеграция. Unit-тесты запускаются автоматически при каждом пул-реквесте. Это предотвращает мерж кода, который ломает существующую логику, без участия человека в проверке.

Какие виды тестов существуют и где место unit-тестов?

Unit тесты это основание тестовой пирамиды – их должно быть больше всего, они должны выполняться быстрее всего и покрывать максимальную часть бизнес-логики.

Google рекомендует соотношение 70/20/10: 70% unit-тестов, 20% интеграционных, 10% UI. На практике многие команды начинают с нуля и двигаются к этому соотношению итеративно, начиная с покрытия ViewModel и UseCase.

Правило: если тест требует запуска на эмуляторе или устройстве – это уже не unit-тест. Unit-тест работает исключительно на JVM, без Android-зависимостей. Все, что требует Context, Activity или Fragment, тестируется через Robolectric или в androidTest.

Что тестировать, а что – нет?

Unit тесты что это в контексте Android – это тесты бизнес-логики, а не фреймворка. Юнит тесты это всегда про то, что делает ваш код, а не как устроен код фреймворка. Хорошее правило: тестируйте то, что написали вы, а не то, что написал Google или JetBrains.

ТестироватьНе тестировать
Логику ViewModel (состояние, события)Стандартные классы SDK (Activity, Fragment)
UseCase / InteractorСторонние библиотеки (Retrofit, Room)
Маппинг данных (DTO → Domain)Автогенерированный код
Валидацию форм и бизнес-правилаПростые геттеры/сеттеры без логики
Обработку ошибок и edge casesКонстанты и data-классы без поведения

Пример хорошего кандидата для теста: метод calculateDiscount(price, userType) в вашем прайсинговом UseCase. Пример плохого кандидата: геттер getName() в data-классе User без какой-либо логики.

Инструменты для unit-тестирования Android-приложений

Стандартный стек для unit-тестирования Android в 2024-2025 году:

ИнструментНазначениеКогда использовать
JUnit 4 / JUnit 5Базовый фреймворк: @Test, @Before, @After, assertionsВсегда – основа любого unit-теста
Mockito + mockito-kotlinСоздание моков, стаббинг, верификация вызововJava/Kotlin-проекты, стандартный мокинг
MockKМокинг-фреймворк для Kotlin: object, companion, coroutinesKotlin-проекты, тесты корутин и Flow
TurbineТестирование Kotlin Flow и StateFlowКогда ViewModel использует Flow
kotlinx-coroutines-testTestDispatcher, runTest для тестов корутинЛюбой асинхронный код на корутинах
RobolectricJVM-симуляция Android-среды без эмулятораКогда нужен Context, но не UI

Зависимости для build.gradle (Kotlin DSL):


testImplementation("junit:junit:4.13.2")
testImplementation("io.mockk:mockk:1.13.12")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.1")
testImplementation("app.cash.turbine:turbine:1.1.0")

Официальная документация по локальным тестам доступна на стнанице developer.android.com/training/testing/local-tests, а детальное сравнение MockK и Mockito для Kotlin: MockK vs Mockito и Mockito.

Структура проекта: куда писать тесты?

Unit тесты Android-проекта живут в строго определенных директориях – и путать их нельзя:

ДиректорияТип тестовГде запускаетсяПример
app/src/test/javaUnit-тесты (local tests)JVM, без AndroidViewModelTest, UseCaseTest
app/src/androidTest/javaИнструментальные тестыЭмулятор / устройствоEspressoTest, RoomTest

app/src/test/java – здесь живут unit-тесты. Они не требуют Android SDK, запускаются на чистой JVM. Файл LoginViewModelTest.kt должен быть именно здесь. Если случайно положить unit-тест в androidTest – он будет требовать эмулятор и работать в 10–20 раз медленнее.

app/src/androidTest/java – инструментальные тесты: Espresso, тесты Room с реальной базой, интеграционные тесты через Hilt. Они медленнее, но позволяют тестировать компоненты, которые невозможно проверить без Android-окружения.

Правило: если тест можно написать без Context и без Android-зависимостей – он относится в test/java. Если нет – в androidTest/java.

Как написать unit-тест?

Разберем написание unit тестов на примере ViewModel с репозиторием. Допустим, есть UserViewModel, который загружает пользователя по ID через UserRepository. Написание unit тестов начинается с паттерна AAA: Arrange → Act → Assert.


class UserViewModelTest {
 
	private val repository = mockk()
	private lateinit var viewModel: UserViewModel
 
	@Before
	fun setup() {
    	viewModel = UserViewModel(repository)
	}
 
	@Test
	fun `loadUser emits user when repository returns success`() = runTest {
    	// Arrange
    	val user = User(id = 1, name = "Anna")
    	coEvery { repository.getUser(1) } returns Result.success(user)
 
    	// Act
    	viewModel.loadUser(1)
 
    	// Assert
    	assertEquals(user, viewModel.uiState.value.user)
	}
}

Что здесь происходит: создается мок репозитория через mockk<>(). В методе setup() инициализируется ViewModel с мок-зависимостью. В тесте через coEvery задается поведение мока – что вернуть при вызове getUser(1). Затем вызывается тестируемый метод и через assertEquals проверяется результат.

Как писать unit тесты для Flow и StateFlow – используйте Turbine. Вместо assertEquals на .value, собирайте эмиссии через turbine.awaitItem():


viewModel.uiState.test {
	val item = awaitItem()
	assertEquals(user, item.user)
	cancelAndIgnoreRemainingEvents()
}

Частые ошибки при написании unit-тестов

Модульное тестирование это не просто запись кода с аннотацией @Test. Типичные ошибки, которые снижают ценность тестов:

ОшибкаПоследствиеКак правильно
Тестировать реализацию, а не поведениеТест ломается при любом рефакторинге, даже если поведение не изменилосьПроверяйте входные данные → выходные данные, а не порядок вызовов методов
Использовать relaxed = true в MockK вездеПропущенные NPE и скрытые баги – мок молча возвращает дефолтные значенияЯвно задавайте поведение через coEvery/every для каждого вызова
Один тест проверяет несколько сценариевПри падении непонятно, что сломалось; тест сложно читатьОдин тест – один сценарий. Используйте параметризованные тесты для похожих случаев
Не проверять edge cases и ошибкиПродакшн падает на пустых списках, null и сетевых ошибкахВсегда пишите тесты на success + error + empty state
Класть unit-тесты в androidTest/Тесты запускаются на эмуляторе – медленно, нестабильно, не в CIUnit-тесты – только в src/test/java, без Android-зависимостей

Заключение

Unit тестирование – это инвестиция, которая окупается с каждым рефакторингом, новой фичей и исправлением бага. Правильно выстроенный стек (JUnit + MockK + Turbine + coroutines-test) и четкое разделение test/ vs androidTest/ делают написание тестов быстрым и предсказуемым. Начните с ViewModel и UseCase – это принесет максимальный эффект при минимальных усилиях. Если вы планируете Android-приложение с нуля и хотите заложить правильную архитектуру с первого дня, ознакомьтесь с вариантами разработки мобильных приложений на Android под ключ.

Часто задаваемые вопросы

Да – особенно в небольшом. Чем меньше проект, тем быстрее написать тесты и тем болезненнее каждый необнаруженный баг. Минимальный набор: тест для каждого UseCase и ViewModel. Даже 20-30 тестов дают уверенность при рефакторинге. Полезный обзор инструментов для разработки Android/iOS с нуля – в материале «Топ-10 программ для создания приложений на Android и iOS».

Правый клик на папке test → Run Tests. Или через терминал: ./gradlew test. Для конкретного класса: ./gradlew testDebugUnitTest --tests "com.example.LoginViewModelTest". Результаты появляются в панели Run – зеленый = прошел, красный = упал с деталями.

После прохождения всех тестов и финальной сборки – в Google Play. Пошаговый процесс публикации описан в материале «Как опубликовать приложение в Google Play». Если нужна помощь с архитектурой, тестированием и выпуском – разработка приложений под ключ включает все этапы от проектирования до публикации.

11 сентября 2026
5 / 5 (1 голос)