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. Например, може да се провери, че данните се запазват при завъртане на екрана (пресъздаване на Activity) и се възстановяват след унищожение.
ViewMatchers са набор от методи от класа Espresso.onView, които позволяват намиране на View на екрана по различни критерии: идентификатор на ресурс (R.id), текст, hint-указание, родителски елемент и йерархия. Ако един matcher не дава уникален резултат, matcher-ите се комбинират чрез allOf().
| Matcher | Предназначение |
|---|---|
| withId(R.id.name) | Търсене по ID на ресурс |
| withText(„текст“) | Търсене по показан текст |
| withHint(„указание“) | Търсене по hint атрибут на EditText |
| isDisplayed() | Проверка дали елементът е видим на екрана |
| hasSibling(matcher) | Търсене по съседен елемент |
| allOf(m1, m2) | Комбинация от няколко matcher-а |
Ако на екрана има няколко еднакви елемента (например, два TextView с различен текст), удобно е да се комбинират matcher-ите чрез 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 matcher-и за намиране на елемента по полета на модела на данните.
ViewAssertions проверяват дали View се намира в определено състояние. Основният метод — matches(matcher) — проверява дали елементът съвпада с дадения matcher. Допълнително, Espresso предлага doesNotExist() (елементът не съществува) и selectedDescendantsMatch() (проверка на вложени елементи).
Най-често срещаните проверки в UI тестове: елементът се показва (isDisplayed), елементът съдържа определен текст (withText), елементът е активен (isEnabled), елементът не е избран (isNotChecked). Всяка проверка хвърля подробно изключение при неуспех — с посочване на View йерархията на екрана. Това опрощава отлавянето: в съобщението за грешка се вижда кои елементи реално са били на екрана в момента на проверката.
Ако стандартните проверки не са достатъчни, може да създадете собствена чрез интерфейса ViewAssertion. Персонализираният assertion получава View и може да провери неговото състояние програмен — например, цвят на текста, полета или състояние на персонализиран компонент, който не е изложен чрез стандартните matcher-и.
// Проверка: 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 (допълнителни matcher-и за 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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също