En este documento, se explica cómo completar tareas comunes de pruebas automatizadas con el API de Espresso.
La API de Espresso alienta a los autores de pruebas a pensar en términos de cómo podría un usuario
hacer mientras interactúa con la aplicación: ubicar elementos de la IU e interactuar
con ellos. Al mismo tiempo, el framework impide el acceso directo a las actividades
y vistas de la aplicación, ya que conservar estos objetos y operar
desde el subproceso de IU es una gran fuente
de inconsistencias en las pruebas. Por lo tanto,
no verás métodos como getView()
y getCurrentActivity()
en la API de Espresso.
Puedes seguir operando de forma segura en vistas implementando tus propias subclases de
ViewAction
y ViewAssertion
.
Componentes de la API
Estos son algunos de los componentes principales de Espresso:
- Espresso: Punto de entrada a las interacciones con las vistas (mediante
onView()
yonData()
). También expone las APIs que no están necesariamente vinculadas a ninguna vista, como comopressBack()
. - ViewMatchers: Es una colección de objetos que implementan el
Matcher<? super View>
. Puedes pasar uno o más de estos a laonView()
para ubicar una vista dentro de la jerarquía de vistas actual. - ViewActions: Es una colección de objetos
ViewAction
a los que se puede pasar. el métodoViewInteraction.perform()
, comoclick()
- ViewAssertions: Es una colección de objetos
ViewAssertion
que se pueden pasó el métodoViewInteraction.check()
. La mayoría de las veces, usarás coincide con la aserción, que usa un comparador de vistas para confirmar el estado del vista seleccionada actualmente.
Ejemplo:
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()));
Cómo encontrar una vista
En la gran mayoría de los casos, el método onView()
toma un comparador de Hamcrest.
que se espera que coincida con una sola vista en la vista actual
en la nube. Los comparadores son eficaces y serán familiares para quienes utilizaron
con Mockito o JUnit. Si no está familiarizado con los comparadores de Hamcrest,
para comenzar con una mirada rápida a esto
presentación.
A menudo, la vista deseada tiene un R.id
único, y un comparador withId
simple
reducir la búsqueda de vistas. Sin embargo, hay muchos casos legítimos en los que
no se puede determinar R.id
en el momento del desarrollo de la prueba. Por ejemplo, la vista específica
Es posible que no tenga un R.id
o que el R.id
no sea único. Esto puede hacer que
las pruebas de instrumentación son frágiles y complicadas de escribir porque la forma normal de
Acceder a la vista con findViewById()
no funciona. Por lo tanto, podrías
necesitan acceder a miembros privados de la actividad o el fragmento que conservan la vista o
busca un contenedor con un R.id
conocido y navega a su contenido para el
en una vista en particular.
Espresso se encarga de este problema correctamente, ya que te permite acotar la vista.
con objetos ViewMatcher
existentes o tus propios objetos personalizados.
Encontrar una vista por su R.id
es tan simple como llamar a onView()
:
Kotlin
onView(withId(R.id.my_view))
Java
onView(withId(R.id.my_view));
A veces, los valores de R.id
se comparten entre varias vistas. Cuando esto sucede, se
Si intentas usar un R.id
específico, obtendrás una excepción, como
AmbiguousViewMatcherException
El mensaje de excepción te proporciona un texto
de la jerarquía de vistas actual, que puedes buscar y encontrar
las vistas que coinciden con R.id
no único:
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****
Si revisas los distintos atributos de las vistas, es posible que encuentres
propiedades identificables. En el ejemplo anterior, una de las vistas tiene el texto
"Hello!"
Puedes usar esta opción para acotar tu búsqueda usando la combinación
comparadores:
Kotlin
onView(allOf(withId(R.id.my_view), withText("Hello!")))
Java
onView(allOf(withId(R.id.my_view), withText("Hello!")));
También puedes optar por no revertir ninguno de los comparadores:
Kotlin
onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))))
Java
onView(allOf(withId(R.id.my_view), not(withText("Unwanted"))));
Consulta ViewMatchers
para los comparadores de vistas que proporciona Espresso.
Consideraciones
- En una aplicación que se comporta correctamente, todas las vistas con las que un usuario puede interactuar
Debe incluir texto descriptivo o incluir una descripción del contenido. Consulta
Cómo mejorar la accesibilidad de las apps
más detalles. Si no puedes acotar una búsqueda con
withText()
owithContentDescription()
, considera tratarlo como un error de accesibilidad. - Usa el comparador menos descriptivo que encuentre la vista que estás buscando.
. No especifiques en exceso, ya que esto forzará al framework a hacer más trabajo que
es necesaria. Por ejemplo, si una vista se identifica de forma exclusiva por su texto,
no es necesario especificar que la vista también se puede asignar desde
TextView
. Para muchos de los vistas, elR.id
de la vista debería ser suficiente. - Si la vista de destino está dentro de un
AdapterView
, comoListView
,GridView
oSpinner
: Es posible que el métodoonView()
no funcione. En estas debes usaronData()
en su lugar.
Cómo ejecutar una acción en una vista
Cuando hayas encontrado un comparador adecuado para la vista de destino,
realizar instancias de ViewAction
en ella con el método de rendimiento
Por ejemplo, para hacer clic en la vista:
Kotlin
onView(...).perform(click())
Java
onView(...).perform(click());
Puedes ejecutar más de una acción con una llamada:
Kotlin
onView(...).perform(typeText("Hello"), click())
Java
onView(...).perform(typeText("Hello"), click());
Si la vista con la que trabajas se encuentra dentro de un ScrollView
(vertical o
horizontal), considera preceder las acciones que requieren que la vista se
se muestra, como click()
y typeText()
, con scrollTo()
. Esta
garantiza que la vista se muestre antes de continuar con la otra acción:
Kotlin
onView(...).perform(scrollTo(), click())
Java
onView(...).perform(scrollTo(), click());
Consulta ViewActions
para las acciones de vista proporcionadas por Espresso.
Cómo verificar las aserciones de vistas
Las aserciones se pueden aplicar a la vista seleccionada con el método check()
.
. La aserción más utilizada es matches()
, Utiliza un
ViewMatcher
para confirmar el estado de la vista seleccionada.
Por ejemplo, para verificar que una vista tenga el texto "Hello!"
, puede usarse el código siguiente:
Kotlin
onView(...).check(matches(withText("Hello!")))
Java
onView(...).check(matches(withText("Hello!")));
Si deseas confirmar que "Hello!"
sea el contenido de la vista, no es recomendable que uses el código siguiente:
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()));
Por otro lado, si deseas afirmar que una vista con el texto "Hello!"
es
visible (por ejemplo, después de un cambio en la marca de visibilidad de vistas), el
código está bien.
Cómo visualizar una prueba de aserción simple
En este ejemplo, SimpleActivity
contiene un Button
y una TextView
. Cuando
el botón, el contenido de TextView
cambia a "Hello Espresso!"
.
A continuación, se explica cómo probar el procedimiento anterior con Espresso:
Haz clic en el botón
El primer paso es buscar una propiedad que ayude a encontrar el botón. El
del botón en SimpleActivity
tiene un R.id
único, como se esperaba.
Kotlin
onView(withId(R.id.button_simple))
Java
onView(withId(R.id.button_simple));
Para hacer clic:
Kotlin
onView(withId(R.id.button_simple)).perform(click())
Java
onView(withId(R.id.button_simple)).perform(click());
Cómo verificar el texto de TextView
La TextView
con el texto para verificar también tiene un R.id
único:
Kotlin
onView(withId(R.id.text_simple))
Java
onView(withId(R.id.text_simple));
Para verificar el texto del contenido:
Kotlin
onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")))
Java
onView(withId(R.id.text_simple)).check(matches(withText("Hello Espresso!")));
Cómo verificar la carga de datos en las vistas de adaptador
AdapterView
es un tipo especial de widget que carga los datos de forma dinámica desde
un adaptador. El ejemplo más común de una AdapterView
es ListView
. Como
a diferencia de los widgets estáticos, como LinearLayout
, solo un subconjunto de la
Se pueden cargar AdapterView
elementos secundarios en la jerarquía de vistas actual. Un sencillo
La búsqueda de onView()
no encontró las vistas que no están cargadas.
Para controlar esto, Espresso proporciona un punto de entrada onData()
independiente, que es
cargar por primera vez el elemento adaptador en cuestión y ponerlo en foco antes de
que opera en ella o en cualquiera de sus hijos.
Advertencia: Implementaciones personalizadas de
Es posible que AdapterView
tenga problemas con onData()
si no cumplen con los contratos de herencia, en particular
API de getItem()
. En esos casos, lo mejor es
refactorizar el código de la aplicación. Si no puedes hacerlo, puedes implementar un
AdapterViewProtocol
personalizado que coincide. Para obtener más información,
mira la configuración predeterminada
Clase AdapterViewProtocols
proporcionada por Espresso.
Cómo implementar una prueba simple de la vista de adaptador
En esta prueba simple, se demuestra cómo usar onData()
. SimpleActivity
contiene un
Spinner
con algunos elementos que representan tipos de cafés. Cuando un
elemento seleccionado, hay un TextView
que cambia a "One %s a day!"
, donde
%s
representa el elemento seleccionado.
El objetivo de esta prueba es abrir Spinner
, seleccionar un elemento específico
Verifica que TextView
contenga el elemento. Como la clase Spinner
se basa
en AdapterView
, se recomienda usar onData()
en lugar de onView()
para
que coinciden con el elemento.
Cómo abrir la selección del elemento
Kotlin
onView(withId(R.id.spinner_simple)).perform(click())
Java
onView(withId(R.id.spinner_simple)).perform(click());
Cómo seleccionar un elemento
Para la selección del elemento, el Spinner
crea una ListView
con el contenido correspondiente.
Esta vista puede ser muy larga y es posible que el elemento no contribuya a ella.
en la nube. Cuando usas onData()
, fuerzamos el elemento deseado en la vista.
en la nube. Los elementos de Spinner
son cadenas, así que queremos hacer coincidir un elemento
que sea igual a la cadena "Americano"
:
Kotlin
onData(allOf(`is`(instanceOf(String::class.java)), `is`("Americano"))).perform(click())
Java
onData(allOf(is(instanceOf(String.class)), is("Americano"))).perform(click());
Verifica que el texto sea correcto
Kotlin
onView(withId(R.id.spinnertext_simple)) .check(matches(withText(containsString("Americano"))))
Java
onView(withId(R.id.spinnertext_simple)) .check(matches(withText(containsString("Americano"))));
Depuración
Espresso proporciona información útil de depuración cuando falla una prueba:
Registros
Espresso registra todas las acciones de vistas en logcat. Por ejemplo:
ViewInteraction: Performing 'single click' action on view with text: Espresso
Jerarquía de vistas
Espresso imprime la jerarquía de vistas en el mensaje de excepción cuando onView()
.
falla.
- Si
onView()
no encuentra la vista de destino, se genera unaNoMatchingViewException
arrojado. Puedes examinar la jerarquía de vistas en la cadena de excepción para analizar por qué el comparador no coincidió con ninguna vista. - Si
onView()
encuentra varias vistas que coinciden con el comparador en cuestión, se producirá una Se arrojaAmbiguousViewMatcherException
. Se muestra la jerarquía de vistas las vistas coincidentes se marcan con la etiquetaMATCHES
:
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****
Cuando se trata de una jerarquía de vistas complicada o de un comportamiento inesperado de los widgets siempre es útil usar la Hierarchy Viewer en Android Studio para una explicación.
Advertencias sobre la vista del adaptador
Espresso advierte a los usuarios sobre la presencia de widgets de AdapterView
. Cuando se produce un onView()
la operación arroja un NoMatchingViewException
y los widgets de AdapterView
son
presente en la jerarquía de vistas, la solución más común es usar onData()
.
El mensaje de excepción incluirá una advertencia con una lista de las vistas de adaptador.
Puedes usar esta información a fin de invocar a onData()
para cargar la vista de destino.
Recursos adicionales
Para obtener más información sobre el uso de Espresso en pruebas de Android, consulta el los siguientes recursos.
Ejemplos
- CustomMatcherSample:
Muestra cómo extender Espresso para que coincida con la propiedad de sugerencia de un objeto
EditText
. - RecyclerViewSample:
Acciones de
RecyclerView
para Espresso. - (más…)