Espresso est un framework de tests d'interface utilisateur automatisés pour applications Android, développé par l'équipe Google et faisant partie d'AndroidX Test. Contrairement aux tests instrumentés qui vérifient des composants isolés, Espresso interagit avec l'interface réelle : il clique sur les boutons, saisit du texte et vérifie l'affichage des éléments. Selon Google Android Developers, Espresso offre une synchronisation automatique avec le thread d'interface utilisateur, éliminant le besoin de Thread.sleep() manuel.
Points essentiels
Espresso est une bibliothèque pour écrire des tests d'interface utilisateur automatisés pour Android, faisant partie de Google AndroidX Test. Elle fournit une API pour trouver des éléments View à l'écran, effectuer des actions sur eux (clic, saisie, balayage) et vérifier leur état (affiché, contient du texte, activé).
La caractéristique clé d'Espresso est la synchronisation automatique avec le thread principal de l'application. Le framework attend la fin de toutes les tâches asynchrones (coroutines, AsyncTask, Handler) avant d'exécuter la vérification suivante. Cela élimine les tests instables causés par les conditions de concurrence et rend les tests d'interface stables et fiables — aucun test ne contient de Thread.sleep() ou de boucles d'attente.
Espresso suit le principe du Chien à Trois Pattes — un test comprend trois étapes : trouver un élément (ViewMatcher), effectuer une action (ViewAction), vérifier le résultat (ViewAssertion). Les trois étapes s'écrivent dans une chaîne d'appels : onView().perform().check(). Ce concept rend les tests prévisibles et faciles à lire — chaque test décrit explicitement ce qu'il cherche, ce qu'il fait et ce qu'il vérifie.
Architecture d'Espresso repose sur trois composants : Espresso (point d'entrée — méthodes statiques onView et onData), ViewMatchers (recherche d'éléments), ViewActions (actions) et ViewAssertions (vérifications). En interne, le framework utilise Idling Resource pour la synchronisation avec le thread d'interface.
Le test le plus simple trouve un bouton par son ID, effectue un clic et vérifie que le texte « Terminé » apparaît. Toutes les opérations sont synchrones du point de vue du test — Espresso garantit que le thread d'interface a terminé le traitement de l'événement avant que le test ne continue. Ceci est réalisé grâce à un mécanisme d'attente intégré : onView bloque l'exécution du test jusqu'à ce que l'interface soit inactive.
@Test
fun buttonClick_showsSuccessText() {
// Trouver le bouton par ID et cliquer
onView(withId(R.id.button_submit))
.perform(click())
// Vérifier que le texte « Terminé » est affiché
onView(withText("Terminé"))
.check(matches(isDisplayed()))
}
Pour lancer un test Espresso, on utilise ActivityScenario (AndroidX Test), qui crée une Activity dans un état spécifique — en cours d'exécution, en pause ou détruite. ActivityScenario permet de tester le cycle de vie de l'Activity en plus des tests d'interface purs. Par exemple, on peut vérifier que les données sont conservées lors de la rotation de l'écran (recréation de l'Activity) et restaurées après destruction.
ViewMatchers est un ensemble de méthodes de la classe Espresso.onView qui permettent de trouver des Views à l'écran selon différents critères : ID de ressource (R.id), texte, indice, élément parent et hiérarchie. Si un matcher ne donne pas de résultat unique, les matchers peuvent être combinés avec allOf().
| Matcher | Fonction |
|---|---|
| withId(R.id.name) | Recherche par ID de ressource |
| withText(« texte ») | Recherche par texte affiché |
| withHint(« indice ») | Recherche par attribut hint d'EditText |
| isDisplayed() | Vérifie que l'élément est visible à l'écran |
| hasSibling(matcher) | Recherche par élément frère |
| allOf(m1, m2) | Combinaison de plusieurs matchers |
Si plusieurs éléments identiques sont présents à l'écran (par exemple, deux TextView avec du texte différent), il est pratique de combiner les matchers avec allOf : onView(allOf(withId(R.id.title), withText(« Bonjour »))). Cela garantit la sélection d'un élément unique. L'opérateur inverse — not() — exclut des éléments de la recherche, et hasSibling() trouve un élément à côté d'un élément connu.
ViewActions sont des actions qu'Espresso effectue sur la View trouvée : click(), typeText(), clearText(), scrollTo(), swipeLeft() et autres. Les actions sont passées à la méthode perform(), qui peut accepter plusieurs actions à la suite.
La méthode perform() accepte vararg ViewAction, permettant d'exécuter une séquence d'actions sur un élément : effacer le champ, saisir un nouveau texte, fermer le clavier et cliquer sur un bouton. Toutes les actions s'exécutent dans l'ordre où elles sont listées, et Espresso garantit que l'action précédente est terminée avant que la suivante ne commence.
// Saisir du texte dans EditText et cliquer sur le bouton
onView(withId(R.id.edit_email))
.perform(
clearText(),
typeText("user@example.com"),
closeSoftKeyboard()
)
onView(withId(R.id.button_login))
.perform(click())
Pour les éléments dans AdapterView (ListView, RecyclerView), on utilise la méthode onData() au lieu de onView. Elle travaille avec les données de l'adaptateur plutôt qu'avec les Views — elle trouve un élément par le contenu du modèle et retourne la View correspondante pour les actions suivantes. onData utilise des matchers hamcrest pour localiser un élément par les champs de données du modèle.
ViewAssertions vérifient qu'une View est dans un état spécifique. La méthode de base — matches(matcher) — vérifie que l'élément correspond au matcher donné. De plus, Espresso propose doesNotExist() (élément absent) et selectedDescendantsMatch() (vérification des éléments imbriqués).
Les vérifications les plus fréquentes dans les tests d'interface : l'élément est affiché (isDisplayed), l'élément contient un texte spécifique (withText), l'élément est activé (isEnabled), l'élément n'est pas coché (isNotChecked). Chaque vérification lève une exception détaillée en cas d'échec — incluant la hiérarchie des Views à l'écran. Cela simplifie le débogage : le message d'erreur montre quels éléments étaient réellement à l'écran au moment de la vérification.
Si les vérifications standard sont insuffisantes, on peut en créer une personnalisée via l'interface ViewAssertion. Une assertion personnalisée reçoit une View et peut vérifier son état par programmation — par exemple, la couleur du texte, les marges intérieures ou l'état d'un composant personnalisé non exposé via les matchers standard.
// Vérification : TextView est affiché et contient du texte
onView(withId(R.id.text_welcome))
.check(matches(isDisplayed()))
.check(matches(withText("Bienvenue")))
// Vérification : l'élément N'est PAS affiché
onView(withId(R.id.progress_bar))
.check(doesNotExist())
Idling Resource est le mécanisme d'Espresso pour synchroniser le test avec les opérations asynchrones. Par défaut, Espresso attend Handler, AsyncTask et les coroutines (via coroutinesIdlingResource). Si l'application effectue un travail en arrière-plan via des threads personnalisés ou des services de rappel, il faut enregistrer une Idling Resource personnalisée.
À partir d'AndroidX Test 1.4.0, Espresso prend en charge les coroutines via CoroutinesIdlingResource. Le test attend automatiquement la fin de toutes les coroutines lancées avant d'effectuer des vérifications d'interface. Pour les scénarios plus complexes, on utilise CountingIdlingResource — un compteur qui s'incrémente au début d'une tâche et se décrémente à la fin.
// Enregistrer IdlingResource pour 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
}
}
Intégration d'Espresso dans un projet Android se fait en ajoutant des dépendances dans le build.gradle au niveau du module. Espresso fait partie d'AndroidX Test, il suffit donc de spécifier les dépendances pour le noyau Espresso, les extensions et l'intégration JUnit. Les tests sont placés dans le répertoire src/androidTest et exécutés sur un appareil physique ou un émulateur via AndroidJUnitRunner.
L'ensemble minimal de dépendances inclut espresso-core (noyau), espresso-contrib (matchers supplémentaires pour RecyclerView, Drawer, Picker) et runner (exécuteur de tests AndroidX). Tous les tests sont exécutés sur un émulateur ou un appareil physique via Android Test Orchestrator.
// build.gradle.kts (dépendances 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")
}
Les tests Espresso peuvent être exécutés via Google Android Test Orchestrator, qui isole chaque test dans un processus séparé et nettoie l'état entre les exécutions. Cela élimine les tests instables causés par les données résiduelles des tests précédents et améliore la stabilité sur les serveurs CI. Pour l'exécution parallèle, on utilise le sharding — répartition des tests entre plusieurs émulateurs.
Questions fréquentes
Espresso fonctionne à l'intérieur du processus de l'application et utilise la synchronisation automatique avec le thread d'interface. UI Automator fonctionne au niveau système, peut interagir avec d'autres applications, mais nécessite une gestion manuelle de l'attente.
C'est une métaphore d'une présentation Google : un test Espresso repose sur trois piliers — ViewMatcher (trouver), ViewAction(agir) et ViewAssertion(vérifier). En retirer un rend le test instable, comme un chien à trois pattes.
Pour RecyclerView, on utilise la bibliothèque espresso-contrib et des méthodes comme onView(withId(R.id.recycler)).perform(actionOnItemAtPosition(0, click())). Une alternative est onData() pour AdapterView ou un ViewAction personnalisé pour trouver un élément par texte dans RecyclerView. De plus, RecyclerViewActions d'espresso-contrib peut être utilisé pour faire défiler jusqu'à un élément et effectuer des actions dessus.
Un test instable est un test qui échoue parfois sans modification du code, à cause de conditions de concurrence ou d'asynchronisme. Espresso résout ce problème avec Idling Resource — attendre la fin de toutes les tâches en arrière-plan avant d'effectuer les vérifications.
Espresso lui-même n'est pas conçu pour les tests de capture d'écran, mais il peut être combiné avec des bibliothèques comme Shot ou Paparazzi. Espresso prépare l'interface dans l'état souhaité, et la bibliothèque de comparaison prend une capture d'écran et la compare à une référence. Cette approche s'appelle le test de régression visuelle et aide à trouver des changements inattendus dans l'interface.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi