Разработка UI-тестов для Android-приложения (Espresso)
Espresso — инструментальный UI-тест-фреймворк от Google, встроенный в Android SDK. Работает в том же процессе, что и приложение, что даёт ему преимущество: синхронизация с UI-потоком автоматическая. Espresso знает, когда приложение «занято» — в отличие от UIAutomator, он не нуждается в sleep() между действиями.
Основы и критичные нюансы
Базовая структура теста Espresso: onView(matcher).perform(action).check(assertion).
@Test
fun loginWithValidCredentials_navigatesToHome() {
onView(withId(R.id.emailInput))
.perform(typeText("[email protected]"), closeSoftKeyboard())
onView(withId(R.id.passwordInput))
.perform(typeText("password123"), closeSoftKeyboard())
onView(withId(R.id.loginButton))
.perform(click())
onView(withId(R.id.homeTitle))
.check(matches(isDisplayed()))
}
Самая частая причина нестабильных тестов — IdlingResource. Если приложение делает асинхронный запрос (Retrofit, Coroutine), Espresso не знает об этом и пытается найти следующий элемент до завершения операции. Решение — зарегистрировать IdlingResource:
// Для OkHttp/Retrofit
val idlingResource = OkHttpIdlingResource.create("okhttp", okHttpClient)
IdlingRegistry.getInstance().register(idlingResource)
Для корутин — IdlingCoroutineDispatcher или EspressoIdlingResource от Google.
Compose UI testing
Если проект использует Jetpack Compose, Espresso-подход заменяется на ComposeTestRule:
@get:Rule
val composeTestRule = createAndroidComposeRule<MainActivity>()
@Test
fun loginScreen_showsErrorOnInvalidEmail() {
composeTestRule.onNodeWithTag("emailInput")
.performTextInput("invalid-email")
composeTestRule.onNodeWithTag("loginButton")
.performClick()
composeTestRule.onNodeWithText("Неверный формат email")
.assertIsDisplayed()
}
testTag в Compose — аналог accessibilityIdentifier в iOS. Обязательно проставляем на все тестируемые элементы через Modifier.testTag("loginButton").
Hilt и DI в инструментальных тестах
Если проект использует Hilt, тесты требуют HiltAndroidRule:
@HiltAndroidTest
class LoginScreenTest {
@get:Rule(order = 0)
val hiltRule = HiltAndroidRule(this)
@get:Rule(order = 1)
val composeTestRule = createAndroidComposeRule<MainActivity>()
@BindValue
val authRepository: AuthRepository = FakeAuthRepository()
@Before
fun init() { hiltRule.inject() }
}
@BindValue позволяет подменить реальный репозиторий на фейковый без изменения основного кода — тест не зависит от сети или базы данных.
Robot pattern (аналог Page Object)
class LoginRobot(private val composeTestRule: AndroidComposeTestRule<*, *>) {
fun enterEmail(email: String) = apply {
composeTestRule.onNodeWithTag("emailInput").performTextInput(email)
}
fun enterPassword(password: String) = apply {
composeTestRule.onNodeWithTag("passwordInput").performTextInput(password)
}
fun clickLogin() = apply {
composeTestRule.onNodeWithTag("loginButton").performClick()
}
fun assertHomeVisible() {
composeTestRule.onNodeWithTag("homeScreen").assertIsDisplayed()
}
}
// Тест читается как сценарий
@Test
fun validLogin_showsHome() {
LoginRobot(composeTestRule)
.enterEmail("[email protected]")
.enterPassword("pass123")
.clickLogin()
.assertHomeVisible()
}
CI: Firebase Test Lab
Локальный эмулятор достаточен для разработки, но перед релизом — Firebase Test Lab с реальными устройствами:
- name: Run Espresso tests on Firebase Test Lab
run: |
gcloud firebase test android run \
--type instrumentation \
--app app/build/outputs/apk/debug/app-debug.apk \
--test app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk \
--device model=Pixel8,version=34 \
--device model=GalaxyS23,version=33
Результаты — Logcat, скриншоты при падении, видео прогона — сохраняются в Google Cloud Storage.
Scope тестирования
Critical flows: логин/регистрация, оплата, основной пользовательский путь. Edge cases: deep link при незалогиненном пользователе, push notification tap, возврат из background. Accessibility: TalkBack через UIAutomator + AccessibilityChecks от Google (AccessibilityChecks.enable() в setUp()).
Срок: 3–5 дней для базового suite critical user flows с Robot pattern, Hilt-интеграцией и CI на Firebase Test Lab.







