Ce document explique comment effectuer des tâches de test automatisées courantes à l'aide du API Espresso.
L'API Espresso encourage les auteurs de tests à réfléchir en termes de ce qu'un utilisateur pourrait
lorsqu'ils interagissent avec l'application : localiser les éléments de l'interface utilisateur et interagir
avec eux. Dans le même temps, le framework empêche l'accès direct aux activités
et des vues de l'application, car conserver ces objets
en dehors du thread UI
est une source importante de fragilité des tests. Vous allez donc
ne voient pas de méthodes telles que getView()
et getCurrentActivity()
dans l'API Espresso.
Vous pouvez toujours opérer en toute sécurité sur les vues en implémentant vos propres sous-classes de
ViewAction
et ViewAssertion
.
Composants de l'API
Les principaux composants d'Espresso sont les suivants:
- Espresso : point d'entrée des interactions avec les vues (via
onView()
etonData()
). Il expose également les API qui ne sont pas nécessairement liées à une vue, comme en tant quepressBack()
. - ViewMatchers : ensemble d'objets qui implémente l'élément
Matcher<? super View>
. Vous pouvez transmettre un ou plusieurs de ces élémentsonView()
pour localiser une vue dans la hiérarchie actuelle des vues. - ViewActions : collection d'objets
ViewAction
pouvant être transmis à la méthodeViewInteraction.perform()
, telle queclick()
. - ViewAssertions : collection d'objets
ViewAssertion
pouvant être transmis la méthodeViewInteraction.check()
. La plupart du temps, vous utiliserez correspond à une assertion, qui utilise un outil de mise en correspondance des vues pour revendiquer l'état la vue actuellement sélectionnée.
Exemple :
Kotlin
// withId(R.id.my_view) is a ViewMatcher // click() is a ViewAction // matches(isDisplayed()) is a ViewAssertion onView(withId(R.id.my_view)) .perform(click()) .check(matches(isDisplayed()))
Java
// withId(R.id.my_view) is a ViewMatcher // click() is a ViewAction // matches(isDisplayed()) is a ViewAssertion onView(withId(R.id.my_view)) .perform(click()) .check(matches(isDisplayed()));
Rechercher une vue
Dans la grande majorité des cas, la méthode onView()
utilise un outil de mise en correspondance hamcrest.
qui doit correspondre à une (et une seule) vue dans la vue actuelle
la hiérarchie. Les outils de mise en correspondance sont puissants, que ceux qui ont déjà utilisé
avec Mockito ou JUnit. Si vous ne connaissez pas les outils de mise en correspondance des hamcrests,
nous vous suggérons de commencer par un aperçu rapide
présentation.
Souvent, la vue souhaitée possède un R.id
unique et un simple outil de mise en correspondance withId
affiner la recherche par vue. Cependant, il existe de nombreux cas légitimes
impossible de déterminer R.id
au moment du développement du test. Par exemple, la vue spécifique
peut ne pas avoir de R.id
, ou R.id
n'est pas unique. Cela peut avoir un impact
les tests d'instrumentation sont fragiles et compliqués à écrire, car la méthode normale
l'accès à la vue, avec findViewById()
, ne fonctionne pas. Vous pouvez donc
besoin d'accéder aux membres privés de l'activité ou du fragment contenant la vue ou
rechercher un conteneur avec un R.id
connu et accéder à son contenu pour la
un point de vue particulier.
Espresso gère parfaitement ce problème en vous permettant d'affiner la vue.
à l'aide d'objets ViewMatcher
existants ou de vos propres objets personnalisés.
Pour rechercher une vue à l'aide de sa R.id
, il suffit d'appeler onView()
:
Kotlin
onView(withId(R.id.my_view))
Java
onView(withId(R.id.my_view));
Parfois, les valeurs R.id
sont partagées entre plusieurs vues. Dans ce cas,
tentative d'utilisation d'un R.id
spécifique vous donne une exception, par exemple :
AmbiguousViewMatcherException
Le message d'exception contient un texte
de la hiérarchie des vues actuelle, que vous pouvez rechercher
les vues qui correspondent à l'élément R.id
non unique:
java.lang.RuntimeException: androidx.test.espresso.AmbiguousViewMatcherException This matcher matches multiple views in the hierarchy: (withId: is <123456789>) ... +----->SomeView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=false, enabled=true, selected=false, is-layout-requested=false, text=, root-is-layout-requested=false, x=0.0, y=625.0, child-count=1} ****MATCHES**** | +------>OtherView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=true, enabled=true, selected=false, is-layout-requested=false, text=Hello!, root-is-layout-requested=false, x=0.0, y=0.0, child-count=1} ****MATCHES****
En parcourant les différents attributs des vues, vous trouverez peut-être
des propriétés identifiables. Dans l'exemple ci-dessus, l'une des vues comporte le texte
"Hello!"
Vous pouvez l'utiliser pour affiner votre recherche en combinant
outils de mise en correspondance:
Kotlin
onView(allOf(withId(R.id.my_view), withText("Hello!")))
Java
onView(allOf(withId(R.id.my_view), withText("Hello!")));
Vous pouvez également choisir de n'inverser aucune mise en correspondance:
Kotlin
onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))))
Java
onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))));
Voir ViewMatchers
pour les outils de mise en correspondance des vues fournis par Espresso.
Points à prendre en compte
- Dans une application performante, toutes les vues avec lesquelles un utilisateur peut interagir
doit contenir un texte descriptif ou une description du contenu. Voir
Rendre les applications plus accessibles pour plus
plus de détails. Si vous ne parvenez pas à affiner une recherche avec
withText()
ouwithContentDescription()
, considérez-le comme un bug d'accessibilité. - Utilisez l'outil de mise en correspondance le moins descriptif pour trouver la vue que vous recherchez
. Ne spécifiez pas trop de détails, car cela forcera le framework à faire plus de travail que
est nécessaire. Par exemple, si une vue est identifiable de manière unique grâce à son texte, vous
il n'est pas nécessaire de spécifier que la vue est également attribuable à partir de
TextView
. Pour de nombreuses vues, leR.id
de la vue devrait être suffisant. - Si la vue cible se trouve à l'intérieur d'un
AdapterView
, tel queListView
,GridView
ouSpinner
: la méthodeonView()
risque de ne pas fonctionner. Dans ces utilisez plutôtonData()
.
Effectuer une action sur une vue
Une fois que vous avez trouvé une mise en correspondance adaptée à la vue cible, vous pouvez
exécuter des instances de ViewAction
sur celui-ci à l'aide de la méthode "perform".
Par exemple, pour cliquer sur la vue:
Kotlin
onView(...).perform(click())
Java
onView(...).perform(click());
Vous pouvez exécuter plusieurs actions avec un seul appel "perform" :
Kotlin
onView(...).perform(typeText("Hello"), click())
Java
onView(...).perform(typeText("Hello"), click());
Si la vue avec laquelle vous travaillez se trouve dans un ScrollView
(vertical ou
(horizontale), envisagez les actions précédentes qui nécessitent que la vue soit
(click()
et typeText()
, par exemple) par scrollTo()
. Ce
garantit que la vue est affichée avant de procéder à l'autre action:
Kotlin
onView(...).perform(scrollTo(), click())
Java
onView(...).perform(scrollTo(), click());
Voir ViewActions
pour les actions d'affichage fournies par Espresso.
Vérifier les assertions de la vue
Vous pouvez appliquer des assertions à la vue actuellement sélectionnée à l'aide de l'check()
. L'assertion la plus utilisée est matches()
. Elle utilise un
ViewMatcher
pour valider l'état de la vue actuellement sélectionnée.
Par exemple, pour vérifier qu'une vue contient le texte "Hello!"
:
Kotlin
onView(...).check(matches(withText("Hello!")))
Java
onView(...).check(matches(withText("Hello!")));
Si vous souhaitez déclarer que "Hello!"
est le contenu de la vue, les pratiques suivantes sont considérées comme déconseillées:
Kotlin
// Don't use assertions like withText inside onView. onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()))
Java
// Don't use assertions like withText inside onView. onView(allOf(withId(...), withText("Hello!"))).check(matches(isDisplayed()));
En revanche, si vous voulez déclarer qu'une vue avec le texte "Hello!"
est
visible (par exemple, après la modification de l'indicateur de visibilité des vues),
le code ne pose pas de problème.
Test simple d'assertion de vue
Dans cet exemple, SimpleActivity
contient un Button
et un TextView
. Lorsque
appuie sur le bouton, le contenu de TextView
devient "Hello Espresso!"
.
Voici comment tester cela avec Espresso:
Cliquez sur le bouton
La première étape consiste à rechercher une propriété permettant de trouver le bouton. La
le bouton dans SimpleActivity
a un R.id
unique, comme prévu.
Kotlin
onView(withId(R.id.button_simple))
Java
onView(withId(R.id.button_simple));
Pour effectuer le clic:
Kotlin
onView(withId(R.id.button_simple)).perform(click())
Java
onView(withId(R.id.button_simple)).perform(click());
Vérifier le texte TextView
Le TextView
avec le texte à vérifier possède également un R.id
unique:
Kotlin
onView(withId(R.id.text_simple))
Java
onView(withId(R.id.text_simple));
Maintenant, vérifions le texte du contenu:
Kotlin
onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")))
Java
onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")));
Vérifier le chargement des données dans les vues de l'adaptateur
AdapterView
est un type particulier de widget qui charge ses données de manière dynamique depuis
un adaptateur. L'exemple le plus courant de AdapterView
est ListView
. En tant que
contrairement aux widgets statiques comme LinearLayout
, seul un sous-ensemble
AdapterView
enfants peuvent être chargés dans la hiérarchie des vues actuelle. Une requête
La recherche onView()
n'a trouvé aucune vue qui n'est actuellement pas chargée.
Espresso gère cela en fournissant un point d'entrée onData()
distinct, qui est
de pouvoir charger l'adaptateur en question, en le mettant au premier plan
l'utiliser ou ses enfants.
Avertissement:Implémentations personnalisées de
AdapterView
peut rencontrer des problèmes avec onData()
.
s'ils rompent les contrats d'héritage, en particulier les
API getItem()
. Dans ce cas, la meilleure chose à faire est de
refactoriser le code de votre application. Si cela n'est pas possible, vous pouvez mettre en œuvre
AdapterViewProtocol
personnalisé correspondant. Pour en savoir plus, consultez
examinez les paramètres par défaut
<ph type="x-smartling-placeholder"></ph>
AdapterViewProtocols
fournie par Espresso.
Test simple de la vue de l'adaptateur
Ce test simple montre comment utiliser onData()
. SimpleActivity
contient un
Spinner
avec quelques éléments représentant des types de boissons au café. Lorsqu'un
est sélectionné, une TextView
devient "One %s a day!"
, où
%s
représente l'élément sélectionné.
L'objectif de ce test est d'ouvrir Spinner
, de sélectionner un élément spécifique, puis
vérifiez que TextView
contient l'élément. La classe Spinner
étant basée sur
AdapterView
, il est recommandé d'utiliser onData()
au lieu de onView()
pour
correspondant à l'article.
Ouvrir la sélection d'éléments
Kotlin
onView(withId(R.id.spinner_simple)).perform(click())
Java
onView(withId(R.id.spinner_simple)).perform(click());
Sélectionner un élément
Pour la sélection d'éléments, Spinner
crée un ListView
avec son contenu.
Cette vue peut être très longue, et l'élément risque de ne pas y contribuer.
la hiérarchie. En utilisant onData()
, nous forçons l'élément souhaité dans la vue.
la hiérarchie. Les éléments dans Spinner
sont des chaînes. Nous voulons donc faire correspondre un élément
égal à la chaîne "Americano"
:
Kotlin
onData(allOf(`is`(instanceOf(String::class.java)), `is`("Americano"))).perform(click())
Java
onData(allOf(is(instanceOf(String.class)), is("Americano"))).perform(click());
Vérifier que le texte est correct
Kotlin
onView(withId(R.id.spinnertext_simple)) .check(matches(withText(containsString("Americano"))))
Java
onView(withId(R.id.spinnertext_simple)) .check(matches(withText(containsString("Americano"))));
Débogage
Espresso fournit des informations de débogage utiles en cas d'échec d'un test:
Journalisation
Espresso consigne toutes les actions d'affichage dans logcat. Exemple :
ViewInteraction: Performing 'single click' action on view with text: Espresso
Hiérarchie de vues
Espresso imprime la hiérarchie des vues dans le message d'exception lorsque onView()
.
est défaillant.
- Si
onView()
ne trouve pas la vue cible, uneNoMatchingViewException
est généré. Vous pouvez examiner la hiérarchie des vues dans la chaîne d'exception à analyser pourquoi la mise en correspondance n'a généré aucune correspondance. - Si
onView()
trouve plusieurs vues qui correspondent à l'outil de mise en correspondance donné, une L'exceptionAmbiguousViewMatcherException
est générée. La hiérarchie des vues est imprimée Les vues avec correspondance sont signalées par le libelléMATCHES
:
java.lang.RuntimeException: androidx.test.espresso.AmbiguousViewMatcherException This matcher matches multiple views in the hierarchy: (withId: is <123456789>) ... +----->SomeView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=false, enabled=true, selected=false, is-layout-requested=false, text=, root-is-layout-requested=false, x=0.0, y=625.0, child-count=1} ****MATCHES**** | +------>OtherView{id=123456789, res-name=plus_one_standard_ann_button, visibility=VISIBLE, width=523, height=48, has-focus=false, has-focusable=true, window-focus=true, is-focused=false, is-focusable=true, enabled=true, selected=false, is-layout-requested=false, text=Hello!, root-is-layout-requested=false, x=0.0, y=0.0, child-count=1} ****MATCHES****
En cas de hiérarchie des vues compliquée ou de comportement inattendu des widgets il est toujours utile d'utiliser Hierarchy Viewer d'Android Studio pour une explication.
Avertissements concernant la vue Adapter
Espresso avertit les utilisateurs de la présence de widgets AdapterView
. Lorsqu'un onView()
génère une exception NoMatchingViewException
, et les widgets AdapterView
sont
dans la hiérarchie des vues, la solution la plus courante consiste à utiliser onData()
.
Le message d'exception comprendra un avertissement avec une liste des vues de l'adaptateur.
Vous pouvez utiliser ces informations pour appeler onData()
afin de charger la vue cible.
Ressources supplémentaires
Pour en savoir plus sur l'utilisation d'Espresso lors des tests sur Android, consultez le les ressources suivantes.
Exemples
- CustomMatcherSample:
Cette page explique comment étendre Espresso pour qu'il corresponde à la propriété d'indice d'un objet
EditText
. - RecyclerViewSample:
Actions
RecyclerView
pour Espresso. - (plus...)