Espresso — это фреймворк для автоматизированного UI-тестирования Android-приложений, разработанный командой Google и входящий в состав AndroidX Test. В отличие от инструментальных тестов, которые проверяют изолированные компоненты, Espresso взаимодействует с реальным UI: нажимает кнопки, вводит текст, проверяет отображение элементов. По данным Google Android Developers, Espresso обеспечивает автоматическую синхронизацию с потоком UI, что устраняет необходимость в ручных Thread.sleep().
Главное
Espresso — это библиотека для написания автоматизированных UI-тестов для Android, которая входит в состав Google AndroidX Test. Она предоставляет API для поиска View-элементов на экране, выполнения действий над ними (нажатие, ввод, свайп) и проверки их состояния (отображается, содержит текст, активен).
Ключевая особенность Espresso — автоматическая синхронизация с главным потоком приложения. Фреймворк ожидает завершения всех асинхронных задач (корутины, AsyncTask, Handler) перед выполнением следующей проверки. Это исключает flaky-тесты, связанные с race condition, и делает UI-тесты стабильными и надёжными — ни один тест не содержит Thread.sleep() или циклов ожидания.
Espresso следует принципу Three-Legged Dog — тест состоит из трёх шагов: найти элемент (ViewMatcher), выполнить действие (ViewAction), проверить результат (ViewAssertion). Все три шага записываются в цепочку вызовов onView().perform().check(). Эта концепция делает тесты предсказуемыми и легко читаемыми — каждый тест явно описывает, что ищет, что делает и что проверяет.
Архитектура Espresso основана на трёх компонентах: Espresso (точка входа — статические методы onView и onData), ViewMatchers (поиск элементов), ViewActions (действия) и ViewAssertions (проверки). Внутри фреймворк использует Idling Resource для синхронизации с UI-потоком.
Простейший тест находит кнопку по ID, выполняет нажатие и проверяет, что появился текст «Готово». Все операции выполняются синхронно с точки зрения теста — Espresso гарантирует, что UI-поток завершил обработку события до того, как тест продолжит выполнение. Это достигается за счёт встроенного механизма ожидания: onView блокирует выполнение теста, пока UI не станет стабильным.
@Test
fun buttonClick_showsSuccessText() {
// Найти кнопку по ID и нажать
onView(withId(R.id.button_submit))
.perform(click())
// Проверить, что текст "Готово" отображается
onView(withText("Готово"))
.check(matches(isDisplayed()))
}
Для запуска Espresso-теста используется ActivityScenario (AndroidX Test), который создаёт Activity в нужном состоянии — запущенная, приостановлена, уничтожена. ActivityScenario позволяет тестировать жизненный цикл Activity в дополнение к чистому UI. Например, можно проверить, что данные сохраняются при повороте экрана (recreation Activity) и восстанавливаются после уничтожения.
ViewMatchers — это набор методов из класса Espresso.onView, которые позволяют найти View на экране по разным критериям: идентификатору ресурса (R.id), тексту, hint-подсказке, родительскому элементу и иерархии. Если один матчер не даёт уникального результата, матчеры комбинируются через allOf().
| Матчер | Назначение |
|---|---|
| withId(R.id.name) | Поиск по ID ресурса |
| withText("текст") | Поиск по отображаемому тексту |
| withHint("подсказка") | Поиск по hint-атрибуту EditText |
| isDisplayed() | Проверка, что элемент видим на экране |
| hasSibling(matcher) | Поиск по соседнему элементу |
| allOf(m1, m2) | Комбинация нескольких матчеров |
Если на экране несколько одинаковых элементов (например, два TextView с разным текстом), удобно комбинировать матчеры через allOf: onView(allOf(withId(R.id.title), withText("Привет"))). Это гарантирует выбор единственного элемента. Обратный оператор — not() — исключает элементы из поиска, а hasSibling() ищет элемент рядом с известным.
ViewActions — это действия, которые Espresso выполняет над найденным View: click(), typeText(), clearText(), scrollTo(), swipeLeft() и другие. Действия передаются в метод perform(), который может принимать несколько действий подряд.
Метод perform() принимает vararg ViewAction, что позволяет выполнить последовательность действий над одним элементом: очистить поле, ввести новый текст, закрыть клавиатуру и нажать кнопку. Все действия выполняются в порядке их перечисления, а Espresso гарантирует, что предыдущее действие завершено до начала следующего.
// Ввод текста в EditText и нажатие кнопки
onView(withId(R.id.edit_email))
.perform(
clearText(),
typeText("user@example.com"),
closeSoftKeyboard()
)
onView(withId(R.id.button_login))
.perform(click())
Для элементов внутри AdapterView (ListView, RecyclerView) используется метод onData() вместо onView. Он работает с данными адаптера, а не с View — находит элемент по содержимому модели и возвращает соответствующий View для дальнейших действий. onData использует hamcrest-матчеры для поиска элемента по полям модели данных.
ViewAssertions проверяют, что View находится в определённом состоянии. Базовый метод — matches(matcher), который проверяет, что элемент соответствует заданному матчеру. Дополнительно Espresso предлагает doesNotExist() (элемент отсутствует) и selectedDescendantsMatch() (проверка вложенных элементов).
Наиболее частые проверки в UI-тестах: элемент отображается (isDisplayed), элемент содержит определённый текст (withText), элемент активен (isEnabled), элемент не выбран (isNotChecked). Каждая проверка выбрасывает подробное исключение в случае неудачи — с указанием иерархии View на экране. Это упрощает отладку: в сообщении об ошибке видно, какие элементы реально были на экране в момент проверки.
Если стандартных проверок недостаточно, можно создать собственную через интерфейс ViewAssertion. Кастомный assertion получает View и может проверить его состояние программно — например, цвет текста, отступы или состояние кастомного компонента, не раскрытого через стандартные матчеры.
// Проверка: TextView отображается и содержит текст
onView(withId(R.id.text_welcome))
.check(matches(isDisplayed()))
.check(matches(withText("Добро пожаловать")))
// Проверка: элемент НЕ отображается
onView(withId(R.id.progress_bar))
.check(doesNotExist())
Idling Resource — это механизм Espresso для синхронизации теста с асинхронными операциями. По умолчанию Espresso ожидает завершения Handler, AsyncTask и корутин (через coroutinesIdlingResource). Если приложение выполняет фоновую работу через собственные потоки или Callback-сервисы, необходимо зарегистрировать кастомный Idling Resource.
Начиная с AndroidX Test 1.4.0, Espresso поддерживает корутины через CoroutinesIdlingResource. Тест автоматически ожидает завершения всех запущенных корутин, прежде чем выполнять проверки UI. Для более сложных сценариев используются CountingIdlingResource — счётчик, который инкрементируется при старте задачи и декрементируется при завершении.
// Регистрация IdlingResource для OkHttp
class OkHttpIdlingResource(
private val client: OkHttpClient
) : IdlingResource {
private var isIdle = true
private var watcher: IdlingResource.ResourceCallback? = null
override fun getName() = "OkHttp"
override fun isIdleNow() = isIdle
override fun registerIdleTransitionCallback(
callback: IdlingResource.ResourceCallback
) {
watcher = callback
}
}
Подключение Espresso в Android-проект выполняется через добавление зависимостей в build.gradle уровня модуля. Espresso входит в AndroidX Test, поэтому достаточно указать зависимости для ядра Espresso, расширений и JUnit-интеграции. Тесты размещаются в директории src/androidTest и запускаются на физическом устройстве или эмуляторе через AndroidJUnitRunner.
Минимальный набор зависимостей включает espresso-core (ядро), espresso-contrib (дополнительные матчеры для RecyclerView, Drawer, Picker) и runner (тестовый раннер AndroidX). Все тесты запускаются на эмуляторе или физическом устройстве через Android Test Orchestrator.
// build.gradle.kts (androidTest dependencies)
android {
defaultConfig {
testInstrumentationRunner =
"androidx.test.runner.AndroidJUnitRunner"
}
}
dependencies {
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
androidTestImplementation("androidx.test.espresso:espresso-contrib:3.6.1")
androidTestImplementation("androidx.test:runner:1.6.1")
androidTestImplementation("androidx.test:rules:1.6.1")
}
Espresso-тесты можно запускать через Google Android Test Orchestrator, который изолирует каждый тест в отдельный процесс и очищает состояние между запусками. Это устраняет flaky-тесты, связанные с остаточными данными предыдущих тестов, и повышает стабильность на CI-серверах. Для параллельного запуска используется sharding — распределение тестов между несколькими эмуляторами.
Часто задаваемые вопросы
Espresso работает внутри процесса приложения и использует автоматическую синхронизацию с UI-потоком. UI Automator работает на уровне системы, может взаимодействовать с чужими приложениями, но требует ручного управления ожиданием.
Это метафора из презентации Google: тест Espresso стоит на трёх опорах — ViewMatcher (поиск), ViewAction(действие) и ViewAssertion(проверка). Если убрать любую из них, тест теряет устойчивость, как трёхногий пёс.
Для RecyclerView используется библиотека espresso-contrib и методы onView(withId(R.id.recycler)).perform(actionOnItemAtPosition(0, click())). Альтернатива — onData() для AdapterView или кастомный ViewAction для поиска элемента по тексту внутри RecyclerView. Дополнительно можно использовать RecyclerViewActions из espresso-contrib для скролла к элементу и действий над ним.
Flaky-тест — тест, который иногда падает без изменения кода, из-за race condition или асинхронности. Espresso решает эту проблему через Idling Resource — ожидание завершения всех фоновых задач перед выполнением проверки.
Сам Espresso не предназначен для скриншот-тестов, но его можно комбинировать с библиотеками вроде Shot или Paparazzi. Espresso подготавливает UI в нужное состояние, а библиотека сравнения делает скриншот и сравнивает с эталоном. Такой подход называется визуальное регрессионное тестирование и помогает находить неожиданные изменения в интерфейсе.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также