Espresso är ett ramverk för automatiserad UI-testning av Android-applikationer, utvecklat av Google-teamet och en del av AndroidX Test. Till skillnad från instrumentella tester som kontrollerar isolerade komponenter, interagerar Espresso med det verkliga UI:t: trycker på knappar, anger text, kontrollerar visning av element. Enligt Google Android Developers tillhandahåller Espresso automatisk synkronisering med UI-tråden, vilket eliminerar behovet av manuell Thread.sleep().
Huvudpunkter
Espresso är ett bibliotek för att skriva automatiserade UI-tester för Android, som är en del av Google AndroidX Test. Det tillhandahåller API för att hitta View-element på skärmen, utföra åtgärder på dem (klicka, mata in, svepa) och kontrollera deras status (visas, innehåller text, är aktiv).
Den viktigaste funktionen hos Espresso är automatisk synkronisering med applikationens huvudtråd. Ramverket väntar på slutförande av alla asynkrona uppgifter (coroutines, AsyncTask, Handler) innan det utför nästa kontroll. Detta eliminerar flaky-tester relaterade till race condition och gör UI-tester stabila och pålitliga — inget test innehåller Thread.sleep() eller vänteloopar.
Espresso följer principen Three-Legged Dog — ett test består av tre steg: hitta elementet (ViewMatcher), utföra åtgärden (ViewAction), kontrollera resultatet (ViewAssertion). Alla tre stegen skrivs i en kedja av anrop onView().perform().check(). Detta koncept gör tester förutsägbara och lättlästa — varje test beskriver tydligt vad det söker, vad det gör och vad det kontrollerar.
Arkitektur Espresso är baserad på tre komponenter: Espresso (ingångspunkt — statiska metoder onView och onData), ViewMatchers (sökning av element), ViewActions (åtgärder) och ViewAssertions (kontroller). Internt använder ramverket Idling Resource för synkronisering med UI-tråden.
Ett enkelt test hittar en knapp efter ID, utför ett klick och kontrollerar att texten “Klart” visas. Alla operationer utförs synkront ur testets perspektiv — Espresso garanterar att UI-tråden har slutfört händelsebearbetningen innan testet fortsätter. Detta uppnås genom en inbyggd väntemekanism: onView blockerar testutförandet tills UI:t blir stabilt.
@Test
fun buttonClick_showsSuccessText() {
// Hitta knappen efter ID och klicka
onView(withId(R.id.button_submit))
.perform(click())
// Kontrollera att texten “Klart” visas
onView(withText("Klart"))
.check(matches(isDisplayed()))
}
För att starta ett Espresso-test används ActivityScenario (AndroidX Test) som skapar Activity i önskat tillstånd — kör, pausad, förstörd. ActivityScenario möjliggör testning av Activitys livscykel vid sidan av rent UI. Till exempel kan man kontrollera att data sparas vid skärmrotation (rekreation av Activity) och återställs efter förstörelse.
ViewMatchers är en uppsättning metoder från klassen Espresso.onView som gör det möjligt att hitta View på skärmen enligt olika kriterier: resursidentifierare (R.id), text, hint-ledtråd, förälderelement och hierarki. Om en matcher inte ger ett unikt resultat kombineras matcher via allOf().
| Matcher | Syfte |
|---|---|
| withId(R.id.name) | Sökning efter resurs-ID |
| withText(“text”) | Sökning efter visad text |
| withHint(“ledtråd”) | Sökning efter hint-attribut i EditText |
| isDisplayed() | Kontroll av att elementet är synligt på skärmen |
| hasSibling(matcher) | Sökning efter grannelement |
| allOf(m1, m2) | Kombination av flera matchers |
Om det finns flera identiska element på skärmen (till exempel två TextView med olika text), är det praktiskt att kombinera matchers via allOf: onView(allOf(withId(R.id.title), withText(“Hej”))). Detta garanterar val av ett unikt element. Den omvända operatorn — not() — utesluter element från sökningen, och hasSibling() söker ett element bredvid ett känt element.
ViewActions är åtgärder som Espresso utför på det hittade View:t: click(), typeText(), clearText(), scrollTo(), swipeLeft() och andra. Åtgärder skickas till metoden perform() som kan ta emot flera åtgärder i följd.
Metoden perform() accepterar vararg ViewAction, vilket gör det möjligt att utföra en sekvens av åtgärder på ett element: rensa fältet, ange ny text, stäng tangentbordet och tryck på knappen. Alla åtgärder utförs i den ordning de anges, och Espresso garanterar att den föregående åtgärden har slutförts innan nästa påbörjas.
// Ange text i EditText och tryck på knappen
onView(withId(R.id.edit_email))
.perform(
clearText(),
typeText("user@example.com"),
closeSoftKeyboard()
)
onView(withId(R.id.button_login))
.perform(click())
För element inuti AdapterView (ListView, RecyclerView) används metoden onData() istället för onView. Den arbetar med adapterdata, inte med View — hittar elementet efter modellinnehåll och returnerar motsvarande View för vidare åtgärder. onData använder hamcrest-matchers för att hitta elementet efter data-modellens fält.
ViewAssertions kontrollerar om View befinner sig i ett visst tillstånd. Grundmetoden — matches(matcher) — kontrollerar om elementet matchar den angivna matchern. Dessutom erbjuder Espresso doesNotExist() (elementet finns inte) och selectedDescendantsMatch() (kontroll av nästlade element).
De vanligaste kontrollerna i UI-tester: elementet visas (isDisplayed), elementet innehåller viss text (withText), elementet är aktivt (isEnabled), elementet är inte valt (isNotChecked). Varje kontroll kastar ett detaljerat undantag vid misslyckande — med angivelse av View-hierarkin på skärmen. Detta förenklar felsökning: i felmeddelandet syns vilka element som faktiskt fanns på skärmen vid kontrollögonblicket.
Om standardkontrollerna inte räcker kan man skapa en egen via gränssnittet ViewAssertion. En anpassad assertion tar emot View och kan kontrollera dess tillstånd programmatiskt — till exempel textfärg, marginaler eller tillståndet för en anpassad komponent som inte exponeras via standardmatchers.
// Kontroll: TextView visas och innehåller text
onView(withId(R.id.text_welcome))
.check(matches(isDisplayed()))
.check(matches(withText("Välkommen")))
// Kontroll: element visas INTE
onView(withId(R.id.progress_bar))
.check(doesNotExist())
Idling Resource är Espressos mekanism för att synkronisera testet med asynkrona operationer. Som standard väntar Espresso på slutförande av Handler, AsyncTask och coroutines (via coroutinesIdlingResource). Om applikationen utför bakgrundsarbete via egna trådar eller Callback-tjänster måste en anpassad Idling Resource registreras.
Från och med AndroidX Test 1.4.0 stöder Espresso coroutines via CoroutinesIdlingResource. Testet väntar automatiskt på slutförande av alla startade coroutines innan det utför UI-kontroller. För mer komplexa scenarier används CountingIdlingResource — en räknare som ökar vid start av en uppgift och minskar vid slutförande.
// Registrera IdlingResource för 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
}
}
Anslutning av Espresso i ett Android-projekt görs genom att lägga till beroenden i build.gradle på modulnivå. Espresso är en del av AndroidX Test, så det räcker med att ange beroenden för Espresso-kärnan, tillägg och JUnit-integration. Tester placeras i katalogen src/androidTest och körs på en fysisk enhet eller emulator via AndroidJUnitRunner.
Den minimala uppsättningen beroenden inkluderar espresso-core (kärna), espresso-contrib (extra matchers för RecyclerView, Drawer, Picker) och runner (AndroidX testlöpare). Alla tester körs på emulator eller fysisk enhet via 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-tester kan köras via Google Android Test Orchestrator, som isolerar varje test i en separat process och rensar tillstånd mellan körningar. Detta eliminerar flaky-tester relaterade till kvarvarande data från tidigare tester och ökar stabiliteten på CI-servrar. För parallell körning används sharding — distribution av tester mellan flera emulatorer.
Vanliga frågor
Espresso arbetar inom applikationsprocessen och använder automatisk synkronisering med UI-tråden. UI Automator arbetar på systemnivå, kan interagera med andra appar men kräver manuell hantering av väntan.
Det är en metafor från Googles presentation: Espresso-testet står på tre pelare — ViewMatcher (sökning), ViewAction(åtgärd) och ViewAssertion(kontroll). Om någon av dem tas bort förlorar testet stabilitet, som en trebenig hund.
För RecyclerView används biblioteket espresso-contrib och metoderna onView(withId(R.id.recycler)).perform(actionOnItemAtPosition(0, click())). Alternativ — onData() för AdapterView eller en anpassad ViewAction för att söka element efter text i RecyclerView. Dessutom kan RecyclerViewActions från espresso-contrib användas för att scrolla till elementet och utföra åtgärder på det.
Flaky-test är ett test som ibland misslyckas utan kodändringar, på grund av race condition eller asynkronitet. Espresso löser detta problem genom Idling Resource — väntan på slutförande av alla bakgrundsuppgifter innan kontrollen utförs.
Espresso i sig är inte avsett för skärmbildstester, men kan kombineras med bibliotek som Shot eller Paparazzi. Espresso förbereder UI:t i önskat tillstånd, och jämförelsebiblioteket tar en skärmbild och jämför med en referens. Detta tillvägagångssätt kallas visuell regressionstestning och hjälper till att hitta oväntade förändringar i gränssnittet.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också