Espresso este un framework pentru testarea UI automatizată a aplicațiilor Android, dezvoltat de echipa Google și care face parte din AndroidX Test. Spre deosebire de testele instrumentale care verifică componente izolate, Espresso interacționează cu UI-ul real: apasă butoane, introduce text, verifică afișarea elementelor. Conform Google Android Developers, Espresso asigură sincronizarea automată cu firul UI, ceea ce elimină necesitatea Thread.sleep() manual.
Principalul
Espresso este o bibliotecă pentru scrierea testelor UI automatizate pentru Android, care face parte din Google AndroidX Test. Oferă API pentru găsirea elementelor View pe ecran, efectuarea de acțiuni asupra lor (apăsare, introducere, glisare) și verificarea stării lor (afișat, conține text, activ).
Caracteristica cheie a Espresso este sincronizarea automată cu firul principal al aplicației. Framework-ul așteaptă finalizarea tuturor sarcinilor asincrone (corutine, AsyncTask, Handler) înainte de a efectua următoarea verificare. Acest lucru elimină testele flaky legate de race condition și face testele UI stabile și fiabile — niciun test nu conține Thread.sleep() sau bucle de așteptare.
Espresso urmează principiul Three-Legged Dog — testul constă în trei pași: găsește elementul (ViewMatcher), execută acțiunea (ViewAction), verifică rezultatul (ViewAssertion). Toți cei trei pași se scriu în lanțul de apeluri onView().perform().check(). Acest concept face testele previzibile și ușor de citit — fiecare test descrie clar ce caută, ce face și ce verifică.
Arhitectura Espresso se bazează pe trei componente: Espresso (punct de intrare — metodele statice onView și onData), ViewMatchers (căutarea elementelor), ViewActions (acțiuni) și ViewAssertions (verificări). Intern, framework-ul folosește Idling Resource pentru sincronizarea cu firul UI.
Cel mai simplu test găsește butonul după ID, efectuează apăsarea și verifică că a apărut textul „Gata“. Toate operațiile se execută sincron din perspectiva testului — Espresso garantează că firul UI a terminat procesarea evenimentului înainte ca testul să continue. Acest lucru se realizează prin mecanismul încorporat de așteptare: onView blochează execuția testului până când UI devine stabil.
@Test
fun buttonClick_showsSuccessText() {
// Găsirea butonului după ID și apăsare
onView(withId(R.id.button_submit))
.perform(click())
// Verificare că textul „Gata“ este afișat
onView(withText("Gata"))
.check(matches(isDisplayed()))
}
Pentru lansarea testului Espresso se folosește ActivityScenario (AndroidX Test), care creează Activity în starea dorită — pornită, suspendată, distrusă. ActivityScenario permite testarea ciclului de viață Activity pe lângă UI-ul pur. De exemplu, se poate verifica că datele se salvează la rotirea ecranului (recrearea Activity) și se restaurează după distrugere.
ViewMatchers este un set de metode din clasa Espresso.onView care permit găsirea View pe ecran după diferite criterii: identificatorul resursei (R.id), text, sugestie hint, elementul părinte și ierarhie. Dacă un singur matcher nu oferă un rezultat unic, matcher-ele se combină prin allOf().
| Matcher | Destinație |
|---|---|
| withId(R.id.name) | Căutare după ID-ul resursei |
| withText("text") | Căutare după textul afișat |
| withHint("sugestie") | Căutare după atributul hint al EditText |
| isDisplayed() | Verificare că elementul este vizibil pe ecran |
| hasSibling(matcher) | Căutare după elementul vecin |
| allOf(m1, m2) | Combinația mai multor matcher-e |
Dacă pe ecran sunt mai multe elemente identice (de exemplu, două TextView cu text diferit), este convenabil să combinați matcher-ele prin allOf: onView(allOf(withId(R.id.title), withText("Salut"))). Aceasta garantează selectarea unui singur element. Operatorul invers — not() — exclude elementele din căutare, iar hasSibling() caută elementul lângă unul cunoscut.
ViewActions sunt acțiunile pe care Espresso le execută asupra View găsit: click(), typeText(), clearText(), scrollTo(), swipeLeft() și altele. Acțiunile se transmit metodei perform(), care poate accepta mai multe acțiuni consecutive.
Metoda perform() acceptă vararg ViewAction, permițând executarea unei secvențe de acțiuni asupra unui singur element: curățați câmpul, introduceți text nou, închideți tastatura și apăsați butonul. Toate acțiunile se execută în ordinea enumerării, iar Espresso garantează că acțiunea anterioară s-a finalizat înainte de începerea următoarei.
// Introducerea textului în EditText și apăsarea butonului
onView(withId(R.id.edit_email))
.perform(
clearText(),
typeText("user@example.com"),
closeSoftKeyboard()
)
onView(withId(R.id.button_login))
.perform(click())
Pentru elementele din AdapterView (ListView, RecyclerView) se folosește metoda onData() în loc de onView. Aceasta lucrează cu datele adaptorului, nu cu View — găsește elementul după conținutul modelului și returnează View-ul corespunzător pentru acțiuni ulterioare. onData folosește matcher-e hamcrest pentru căutarea elementului după câmpurile modelului de date.
ViewAssertions verifică că View se află într-o anumită stare. Metoda de bază — matches(matcher), care verifică dacă elementul corespunde matcher-ului dat. Suplimentar, Espresso oferă doesNotExist() (elementul lipsește) și selectedDescendantsMatch() (verificarea elementelor imbricate).
Cele mai frecvente verificări în testele UI: elementul este afișat (isDisplayed), elementul conține un anumit text (withText), elementul este activ (isEnabled), elementul nu este selectat (isNotChecked). Fiecare verificare aruncă o excepție detaliată în caz de eșec — cu indicarea ierarhiei View pe ecran. Acest lucru simplifică depanarea: în mesajul de eroare se vede ce elemente erau efectiv pe ecran în momentul verificării.
Dacă verificările standard nu sunt suficiente, puteți crea una proprie prin interfața ViewAssertion. Assertion-ul personalizat primește View și poate verifica starea acestuia programatic — de exemplu, culoarea textului, marginile sau starea unui component personalizat, neexpus prin matcher-e standard.
// Verificare: TextView este afișat și conține text
onView(withId(R.id.text_welcome))
.check(matches(isDisplayed()))
.check(matches(withText("Bun venit")))
// Verificare: elementul NU este afișat
onView(withId(R.id.progress_bar))
.check(doesNotExist())
Idling Resource este mecanismul Espresso pentru sincronizarea testului cu operațiile asincrone. În mod implicit, Espresso așteaptă finalizarea Handler, AsyncTask și corutinelor (prin coroutinesIdlingResource). Dacă aplicația execută lucrări în fundal prin fire proprii sau servicii Callback, este necesar să înregistrați un Idling Resource personalizat.
Începând cu AndroidX Test 1.4.0, Espresso suportă corutinele prin CoroutinesIdlingResource. Testul așteaptă automat finalizarea tuturor corutinelor lansate înainte de a efectua verificări UI. Pentru scenarii mai complexe se folosește CountingIdlingResource — un contor care se incrementează la startul sarcinii și se decrementează la finalizare.
// Înregistrarea IdlingResource pentru 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
}
}
Conectarea Espresso în proiectul Android se face prin adăugarea dependințelor în build.gradle la nivel de modul. Espresso face parte din AndroidX Test, deci este suficient să specificați dependințele pentru nucleul Espresso, extensii și integrarea JUnit. Testele se plasează în directorul src/androidTest și se rulează pe un dispozitiv fizic sau emulator prin AndroidJUnitRunner.
Setul minim de dependințe include espresso-core (nucleu), espresso-contrib (matcher-e suplimentare pentru RecyclerView, Drawer, Picker) și runner (test runner AndroidX). Toate testele se rulează pe emulator sau dispozitiv fizic prin Android Test Orchestrator.
// build.gradle.kts (dependințe androidTest)
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")
}
Testele Espresso pot fi lansate prin Google Android Test Orchestrator, care izolează fiecare test într-un proces separat și curăță starea între lansări. Acest lucru elimină testele flaky legate de datele reziduale ale testelor anterioare și crește stabilitatea pe serverele CI. Pentru lansarea paralelă se folosește sharding — distribuirea testelor între mai multe emulatoare.
Întrebări frecvente
Espresso funcționează în interiorul procesului aplicației și folosește sincronizarea automată cu firul UI. UI Automator funcționează la nivel de sistem, poate interacționa cu alte aplicații, dar necesită gestionarea manuală a așteptării.
Este o metaforă din prezentarea Google: testul Espresso stă pe trei suporturi — ViewMatcher (căutare), ViewAction (acțiune) și ViewAssertion (verificare). Dacă lips oricare dintre ele, testul își pierde stabilitatea, ca un câine cu trei picioare.
Pentru RecyclerView se folosește biblioteca espresso-contrib și metodele onView(withId(R.id.recycler)).perform(actionOnItemAtPosition(0, click())). Alternativa este onData() pentru AdapterView sau un ViewAction personalizat pentru căutarea elementului după text în interiorul RecyclerView. Suplimentar, se pot folosi RecyclerViewActions din espresso-contrib pentru derularea la element și acțiuni asupra lui.
Testul flaky — un test care uneori eșuează fără modificarea codului, din cauza race condition sau asincronismului. Espresso rezolvă această problemă prin Idling Resource — așteptarea finalizării tuturor sarcinilor de fundal înainte de efectuarea verificării.
Espresso în sine nu este destinat testelor de capturi de ecran, dar poate fi combinat cu biblioteci precum Shot sau Paparazzi. Espresso pregătește UI în starea dorită, iar biblioteca de comparație face o captură de ecran și o compară cu etalonul. Această abordare se numește testare vizuală de regresie și ajută la găsirea modificărilor neașteptate în interfață.
Concluzii
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și