Espresso es un framework para pruebas de UI automatizadas de aplicaciones Android, desarrollado por Google y parte de AndroidX Test. A diferencia de las pruebas instrumentadas que verifican componentes aislados, Espresso interactúa con la interfaz real: hace clic en botones, ingresa texto y verifica la visualización de elementos. Según Google Android Developers, Espresso proporciona sincronización automática con el hilo de UI, eliminando la necesidad de Thread.sleep() manual.
Puntos clave
Espresso es una biblioteca para escribir pruebas de UI automatizadas para Android, parte de Google AndroidX Test. Proporciona una API para encontrar elementos View en pantalla, realizar acciones sobre ellos (clic, entrada, deslizamiento) y verificar su estado (visible, contiene texto, habilitado).
La característica clave de Espresso es la sincronización automática con el hilo principal de la aplicación. El framework espera a que todas las tareas asíncronas (corutinas, AsyncTask, Handler) finalicen antes de ejecutar la siguiente verificación. Esto elimina las pruebas inestables causadas por condiciones de carrera y hace que las pruebas de UI sean estables y confiables — ninguna prueba contiene Thread.sleep() o bucles de espera.
Espresso sigue el principio del Perro de Tres Patas: una prueba consta de tres pasos: encontrar un elemento (ViewMatcher), realizar una acción (ViewAction), verificar el resultado (ViewAssertion). Los tres pasos se escriben en una cadena de llamadas: onView().perform().check(). Este concepto hace que las pruebas sean predecibles y fáciles de leer — cada prueba describe explícitamente qué busca, qué hace y qué verifica.
Arquitectura de Espresso se basa en tres componentes: Espresso (punto de entrada — métodos estáticos onView y onData), ViewMatchers (búsqueda de elementos), ViewActions (acciones) y ViewAssertions (verificaciones). Internamente, el framework utiliza Idling Resource para sincronizarse con el hilo de UI.
La prueba más simple encuentra un botón por ID, realiza un clic y verifica que aparezca el texto “Listo”. Todas las operaciones son síncronas desde la perspectiva de la prueba — Espresso garantiza que el hilo de UI haya terminado de procesar el evento antes de que la prueba continúe. Esto se logra mediante un mecanismo de espera integrado: onView bloquea la ejecución de la prueba hasta que la UI esté inactiva.
@Test
fun buttonClick_showsSuccessText() {
// Buscar botón por ID y hacer clic
onView(withId(R.id.button_submit))
.perform(click())
// Verificar que el texto “Listo” se muestre
onView(withText("Listo"))
.check(matches(isDisplayed()))
}
Para lanzar una prueba de Espresso se utiliza ActivityScenario (AndroidX Test), que crea una Activity en un estado específico — ejecutándose, pausada o destruida. ActivityScenario permite probar el ciclo de vida de la Activity además de las pruebas de UI puras. Por ejemplo, se puede verificar que los datos se conservan al rotar la pantalla (recreación de Activity) y se restauran después de la destrucción.
ViewMatchers es un conjunto de métodos de la clase Espresso.onView que permiten encontrar Views en pantalla por varios criterios: ID de recurso (R.id), texto, hint, elemento padre y jerarquía. Si un matcher no produce un resultado único, los matchers se pueden combinar usando allOf().
| Matcher | Propósito |
|---|---|
| withId(R.id.name) | Búsqueda por ID de recurso |
| withText(“texto”) | Búsqueda por texto mostrado |
| withHint(“pista”) | Búsqueda por atributo hint de EditText |
| isDisplayed() | Verifica que el elemento sea visible en pantalla |
| hasSibling(matcher) | Búsqueda por elemento hermano |
| allOf(m1, m2) | Combinación de múltiples matchers |
Si hay múltiples elementos idénticos en pantalla (por ejemplo, dos TextView con diferente texto), es conveniente combinar matchers usando allOf: onView(allOf(withId(R.id.title), withText(“Hola”))). Esto garantiza seleccionar un único elemento. El operador inverso — not() — excluye elementos de la búsqueda, y hasSibling() encuentra un elemento junto a uno conocido.
ViewActions son acciones que Espresso realiza sobre la View encontrada: click(), typeText(), clearText(), scrollTo(), swipeLeft() y otras. Las acciones se pasan al método perform(), que puede aceptar múltiples acciones en secuencia.
El método perform() acepta vararg ViewAction, lo que permite ejecutar una secuencia de acciones sobre un elemento: limpiar el campo, ingresar nuevo texto, cerrar el teclado y hacer clic en un botón. Todas las acciones se ejecutan en el orden en que se enumeran, y Espresso garantiza que la acción anterior se complete antes de que comience la siguiente.
// Ingresar texto en EditText y hacer clic en botón
onView(withId(R.id.edit_email))
.perform(
clearText(),
typeText("user@example.com"),
closeSoftKeyboard()
)
onView(withId(R.id.button_login))
.perform(click())
Para elementos dentro de AdapterView (ListView, RecyclerView), se utiliza el método onData() en lugar de onView. Trabaja con datos del adaptador en lugar de Views — encuentra un elemento por contenido del modelo y devuelve la View correspondiente para acciones posteriores. onData utiliza matchers hamcrest para localizar un elemento por campos de datos del modelo.
ViewAssertions verifican que una View esté en un estado específico. El método básico — matches(matcher) — verifica que el elemento coincida con el matcher dado. Adicionalmente, Espresso ofrece doesNotExist() (elemento ausente) y selectedDescendantsMatch() (verificación de elementos anidados).
Las verificaciones más frecuentes en pruebas de UI: el elemento se muestra (isDisplayed), el elemento contiene texto específico (withText), el elemento está habilitado (isEnabled), el elemento no está seleccionado (isNotChecked). Cada verificación lanza una excepción detallada en caso de fallo — incluyendo la jerarquía de Views en pantalla. Esto simplifica la depuración: el mensaje de error muestra qué elementos estaban realmente en pantalla en el momento de la verificación.
Si las verificaciones estándar son insuficientes, se puede crear una personalizada mediante la interfaz ViewAssertion. Una aserción personalizada recibe una View y puede verificar su estado programáticamente — por ejemplo, color de texto, relleno o el estado de un componente personalizado no expuesto a través de matchers estándar.
// Verificar: TextView se muestra y contiene texto
onView(withId(R.id.text_welcome))
.check(matches(isDisplayed()))
.check(matches(withText("Bienvenido")))
// Verificar: el elemento NO se muestra
onView(withId(R.id.progress_bar))
.check(doesNotExist())
Idling Resource es el mecanismo de Espresso para sincronizar la prueba con operaciones asíncronas. Por defecto, Espresso espera a Handler, AsyncTask y corutinas (a través de coroutinesIdlingResource). Si la aplicación realiza trabajo en segundo plano mediante hilos personalizados o servicios de callback, es necesario registrar un Idling Resource personalizado.
A partir de AndroidX Test 1.4.0, Espresso admite corutinas a través de CoroutinesIdlingResource. La prueba espera automáticamente a que todas las corutinas lanzadas finalicen antes de realizar verificaciones de UI. Para escenarios más complejos, se utiliza CountingIdlingResource — un contador que se incrementa al iniciar una tarea y se decrementa al completarla.
// Registrar IdlingResource para 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
}
}
Integración de Espresso en un proyecto Android se realiza agregando dependencias en el build.gradle a nivel de módulo. Espresso es parte de AndroidX Test, por lo que basta con especificar las dependencias para el núcleo de Espresso, las extensiones y la integración con JUnit. Las pruebas se colocan en el directorio src/androidTest y se ejecutan en un dispositivo físico o emulador mediante AndroidJUnitRunner.
El conjunto mínimo de dependencias incluye espresso-core (núcleo), espresso-contrib (matchers adicionales para RecyclerView, Drawer, Picker) y runner (ejecutor de pruebas de AndroidX). Todas las pruebas se ejecutan en un emulador o dispositivo físico mediante Android Test Orchestrator.
// build.gradle.kts (dependencias 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")
}
Las pruebas de Espresso se pueden ejecutar mediante Google Android Test Orchestrator, que aísla cada prueba en un proceso separado y limpia el estado entre ejecuciones. Esto elimina las pruebas inestables causadas por datos residuales de pruebas anteriores y mejora la estabilidad en servidores CI. Para ejecución paralela, se utiliza sharding — distribución de pruebas entre múltiples emuladores.
Preguntas frecuentes
Espresso funciona dentro del proceso de la aplicación y utiliza sincronización automática con el hilo de UI. UI Automator funciona a nivel del sistema, puede interactuar con otras aplicaciones, pero requiere gestión manual de espera.
Es una metáfora de una presentación de Google: una prueba de Espresso se sostiene sobre tres pilares — ViewMatcher (buscar), ViewAction (actuar) y ViewAssertion (verificar). Si se quita cualquiera de ellos, la prueba pierde estabilidad, como un perro de tres patas.
Para RecyclerView se utiliza la biblioteca espresso-contrib y métodos como onView(withId(R.id.recycler)).perform(actionOnItemAtPosition(0, click())). Una alternativa es onData() para AdapterView o un ViewAction personalizado para buscar un elemento por texto dentro de RecyclerView. Adicionalmente, se pueden usar RecyclerViewActions de espresso-contrib para desplazarse a un elemento y realizar acciones sobre él.
Una prueba inestable es una prueba que a veces falla sin cambios en el código, debido a condiciones de carrera o asincronía. Espresso resuelve este problema mediante Idling Resource — esperando a que todas las tareas en segundo plano se completen antes de realizar verificaciones.
Espresso en sí no está diseñado para pruebas de captura de pantalla, pero se puede combinar con bibliotecas como Shot o Paparazzi. Espresso prepara la UI en el estado deseado, y la biblioteca de comparación toma una captura de pantalla y la compara con una referencia. Este enfoque se llama pruebas de regresión visual y ayuda a encontrar cambios inesperados en la interfaz.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también