El marco de trabajo de pruebas de UI Automator proporciona un conjunto de APIs para compilar pruebas de IU que interactúan con las apps del usuario y las apps del sistema.
Introducción a las pruebas modernas de UI Automator
UI Automator 2.4 presenta un lenguaje específico del dominio (DSL) optimizado y compatible con Kotlin que simplifica la escritura de pruebas de IU para Android. Esta nueva superficie de API se enfoca en la búsqueda de elementos basados en predicados y el control explícito sobre los estados de la app. Úsala para crear pruebas automatizadas más confiables y fáciles de mantener.
UI Automator te permite probar una app desde fuera de su proceso. Esto te permite probar versiones de lanzamiento con la minificación aplicada. UI Automator also ayuda cuando se escriben pruebas de macrobenchmark.
Las funciones clave del enfoque moderno incluyen las siguientes:
- Un alcance de prueba dedicado
uiAutomatorpara un código de prueba más limpio y expresivo - Métodos como
onElement,onElementsyonElementOrNullpara encontrar elementos de la IU con predicados claros - Mecanismo de espera integrado para elementos condicionales
onElement*(timeoutMs: Long = 10000) - Administración explícita del estado de la app, como
waitForStableywaitForAppToBeVisible - Interacción directa con los nodos de la ventana de accesibilidad para situaciones de prueba de varias ventanas
- Funciones integradas de captura de pantalla y un
ResultsReporterpara pruebas visuales y depuración.
Configura tu proyecto
Para comenzar a usar las APIs modernas de UI Automator, actualiza el archivo de tu proyecto
build.gradle.kts para incluir la dependencia más reciente:
Kotlin
dependencies {
...
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.4.0-alpha05")
}
Groovy
dependencies {
...
androidTestImplementation "androidx.test.uiautomator:uiautomator:2.4.0-alpha05"
}
Conceptos básicos de la API
En las siguientes secciones, se describen los conceptos básicos de la API moderna de UI Automator.
El alcance de la prueba uiAutomator
Accede a todas las APIs nuevas de UI Automator dentro del uiAutomator { ... }
bloque. Esta función crea un UiAutomatorTestScope que proporciona un entorno conciso
y con seguridad de tipos para tus operaciones de prueba.
uiAutomator {
// All your UI Automator actions go here
startApp("com.example.targetapp")
onElement { textAsString() == "Hello, World!" }.click()
}
Encuentra elementos de la IU
Usa las APIs de UI Automator con predicados para ubicar elementos de la IU. Estos predicados te permiten definir condiciones para propiedades como texto, estado seleccionado o enfocado, y descripción del contenido.
onElement { predicate }: Muestra el primer elemento de la IU que coincide con el predicado dentro de un tiempo de espera predeterminado. La función arroja una excepción si no ubica un elemento coincidente.// Find a button with the text "Submit" and click it onElement { textAsString() == "Submit" }.click() // Find a UI element by its resource ID onElement { viewIdResourceName == "my_button_id" }.click() // Allow a permission request watchFor(PermissionDialog) { clickAllow() }onElementOrNull { predicate }: Similar aonElement, pero muestranullsi la función no encuentra ningún elemento coincidente dentro del tiempo de espera. No arroja una excepción. Usa este método para elementos opcionales.val optionalButton = onElementOrNull { textAsString() == "Skip" } optionalButton?.click() // Click only if the button existsonElements { predicate }: Espera hasta que al menos un elemento de la IU coincida con el predicado determinado y, luego, muestra una lista de todos los elementos de la IU coincidentes.// Get all items in a list Ui element val listItems = onElements { className == "android.widget.TextView" && isClickable } listItems.forEach { it.click() }
A continuación, se incluyen algunas sugerencias para usar llamadas onElement:
Encadena
onElementllamadas para elementos anidados: Puedes encadenaronElementllamadas para encontrar elementos dentro de otros elementos, siguiendo una jerarquía de elementos superiores y secundarios.// Find a parent Ui element with ID "first", then its child with ID "second", // then its grandchild with ID "third", and click it. onElement { viewIdResourceName == "first" } .onElement { viewIdResourceName == "second" } .onElement { viewIdResourceName == "third" } .click()Para especificar un tiempo de espera para las funciones
onElement*, pasa un valor que represente milisegundos.// Find a Ui element with a zero timeout (instant check) onElement(0) { viewIdResourceName == "something" }.click() // Find a Ui element with a custom timeout of 10 seconds onElement(10_000) { textAsString() == "Long loading text" }.click()
Interactúa con elementos de la IU
Para interactuar con elementos de la IU, simula clics o establece texto en campos editables.
// Click a Ui element
onElement { textAsString() == "Tap Me" }.click()
// Set text in an editable field
onElement { className == "android.widget.EditText" }.setText("My input text")
// Perform a long click
onElement { contentDescription == "Context Menu" }.longClick()
Controla los estados y observadores de la app
Administra el ciclo de vida de tu app y controla los elementos de la IU inesperados que puedan aparecer durante las pruebas.
Administración del ciclo de vida de la app
Las APIs proporcionan formas de controlar el estado de la app en prueba:
// Start a specific app by package name. Used for benchmarking and other
// self-instrumenting tests.
startApp("com.example.targetapp")
// Start a specific activity within the target app
startActivity(SomeActivity::class.java)
// Start an intent
startIntent(myIntent)
// Clear the app's data (resets it to a fresh state)
clearAppData("com.example.targetapp")
Controla la IU inesperada
La API de watchFor te permite definir controladores para elementos de la IU inesperados,
como diálogos de permisos, que pueden aparecer durante el flujo de prueba. Esto
usa el mecanismo de observador interno, pero ofrece más flexibilidad.
import androidx.test.uiautomator.PermissionDialog
@Test
fun myTestWithPermissionHandling() = uiAutomator {
startActivity(MainActivity::class.java)
// Register a watcher to click "Allow" if a permission dialog appears
watchFor(PermissionDialog) { clickAllow() }
// Your test steps that might trigger a permission dialog
onElement { textAsString() == "Request Permissions" }.click()
// Example: You can register a different watcher later if needed
clearAppData("com.example.targetapp")
// Now deny permissions
startApp("com.example.targetapp")
watchFor(PermissionDialog) { clickDeny() }
onElement { textAsString() == "Request Permissions" }.click()
}
PermissionDialog es un ejemplo de ScopedWatcher<T>, en el que T es el
objeto que se pasa como un alcance al bloque en watchFor. Puedes crear observadores personalizados
basados en este patrón.
Espera la visibilidad y la estabilidad de la app
A veces, las pruebas deben esperar a que los elementos se vuelvan visibles o estables. UI Automator ofrece varias APIs para ayudarte con esto.
The waitForAppToBeVisible("com.example.targetapp") espera a que aparezca en la pantalla un elemento de la IU con
el nombre de paquete determinado dentro de un tiempo de espera personalizable.
// Wait for the app to be visible after launching it
startApp("com.example.targetapp")
waitForAppToBeVisible("com.example.targetapp")
Usa la API de waitForStable() para verificar que la IU de la app se considere estable
antes de interactuar con ella.
// Wait for the entire active window to become stable
activeWindow().waitForStable()
// Wait for a specific Ui element to become stable (e.g., after a loading animation)
onElement { viewIdResourceName == "my_loading_indicator" }.waitForStable()
Usa UI Automator para Macrobenchmarks y perfiles de Baseline
Usa UI Automator para pruebas de rendimiento con Jetpack Macrobenchmark y para generar perfiles de Baseline, ya que proporciona una forma confiable de interactuar con tu app y medir el rendimiento desde la perspectiva del usuario final.
Macrobenchmark usa las APIs de UI Automator para controlar la IU y medir las interacciones.
Por ejemplo, en las comparativas de inicio, puedes usar onElement para detectar cuándo se carga por completo el contenido de la IU, lo que te permite medir el tiempo para la visualización completa
(TTFD). En las comparativas de bloqueos, se usan las APIs de UI Automator para desplazar listas o
ejecutar animaciones para medir los tiempos de fotogramas. Las funciones como startActivity() o
startIntent() son útiles para que la app esté en el estado correcto antes de
que comience la medición.
Cuando generas perfiles de Baseline, automatizas los recorridos críticos del usuario
de tu app (CUJs) para registrar qué clases y métodos requieren una compilación previa. UI
Automator es una herramienta ideal para escribir estas secuencias de comandos de automatización. La búsqueda de elementos basada en predicados del DSL moderno
y los mecanismos de espera integrados (onElement)
conducen a una ejecución de pruebas más sólida y determinista en comparación con otros métodos.
Esta estabilidad reduce la inestabilidad y garantiza que el perfil de Baseline generado
refleje con precisión las instrucciones de código ejecutadas durante los flujos de usuario más importantes.
Funciones avanzadas
Las siguientes funciones son útiles para situaciones de prueba más complejas.
Interactúa con varias ventanas
Las APIs de UI Automator te permiten interactuar directamente con los elementos de la IU y examinarlos. Esto es particularmente útil para situaciones que involucran varias ventanas, como el modo de pantalla en pantalla (PiP) o los diseños de pantalla dividida.
// Find the first window that is in Picture-in-Picture mode
val pipWindow = windows()
.first { it.isInPictureInPictureMode == true }
// Now you can interact with elements within that specific window
pipWindow.onElement { textAsString() == "Play" }.click()
Capturas de pantalla y aserciones visuales
Captura capturas de pantalla de toda la pantalla, ventanas específicas o elementos individuales de la IU directamente en tus pruebas. Esto es útil para las pruebas de regresión visual y la depuración.
uiautomator {
// Take a screenshot of the entire active window
val fullScreenBitmap: Bitmap = activeWindow().takeScreenshot()
fullScreenBitmap.saveToFile(File("/sdcard/Download/full_screen.png"))
// Take a screenshot of a specific UI element (e.g., a button)
val buttonBitmap: Bitmap = onElement { viewIdResourceName == "my_button" }.takeScreenshot()
buttonBitmap.saveToFile(File("/sdcard/Download/my_button_screenshot.png"))
// Example: Take a screenshot of a PiP window
val pipWindowScreenshot = windows()
.first { it.isInPictureInPictureMode == true }
.takeScreenshot()
pipWindowScreenshot.saveToFile(File("/sdcard/Download/pip_screenshot.png"))
}
La función de extensión saveToFile para Bitmap simplifica el guardado de la imagen capturada
en una ruta de acceso especificada.
Usa ResultsReporter para la depuración
El ResultsReporter te ayuda a asociar artefactos de prueba, como capturas de pantalla,
directamente con los resultados de la prueba en Android Studio para facilitar la inspección y
la depuración.
uiAutomator {
startApp("com.example.targetapp")
val reporter = ResultsReporter("MyTestArtifacts") // Name for this set of results
val file = reporter.addNewFile(
filename = "my_screenshot",
title = "Accessible button image" // Title that appears in Android Studio test results
)
// Take a screenshot of an element and save it using the reporter
onElement { textAsString() == "Accessible button" }
.takeScreenshot()
.saveToFile(file)
// Report the artifacts to instrumentation, making them visible in Android Studio
reporter.reportToInstrumentation()
}
Migra desde versiones anteriores de UI Automator
Si tienes pruebas de UI Automator existentes escritas con superficies de API anteriores, usa la siguiente tabla como referencia para migrar al enfoque moderno:
| Tipo de acción | Método anterior de UI Automator | Método nuevo de UI Automator |
|---|---|---|
| Punto de entrada | UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) |
Encapsula la lógica de prueba en el alcance uiAutomator { ... }. |
| Encuentra elementos de la IU | device.findObject(By.res("com.example.app:id/my_button")) |
onElement { viewIdResourceName == "my\_button" } |
| Encuentra elementos de la IU | device.findObject(By.text("Click Me")) |
onElement { textAsString() == "Click Me" } |
| Espera la IU inactiva | device.waitForIdle() |
Prefiere el mecanismo de tiempo de espera integrado de onElement; de lo contrario, activeWindow().waitForStable(). |
| Encuentra elementos secundarios | Llamadas findObject anidadas manualmente |
onElement().onElement() encadenamiento |
| Controla los diálogos de permisos | UiAutomator.registerWatcher() |
watchFor(PermissionDialog) |