El framework de prueba 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 de dominio (DSL) optimizado y compatible con Kotlin que simplifica la escritura de pruebas de IU para Android. Esta nueva plataforma de API se enfoca en la búsqueda de elementos basada 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 también ayuda a escribir pruebas de macrocomparativas.
Las funciones clave del enfoque moderno incluyen las siguientes:
- Un alcance de prueba
uiAutomator
dedicado para un código de prueba más limpio y expresivo - Métodos como
onElement
,onElements
yonElementOrNull
para 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
waitForStable
ywaitForAppToBeVisible
- 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
ResultsReporter
para pruebas visuales y depuración.
Configura tu proyecto
Para comenzar a usar las APIs modernas de UI Automator, actualiza el archivo build.gradle.kts
de tu proyecto 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 de UI Automator moderna.
El alcance de la prueba de uiAutomator
Accede a todas las nuevas APIs de UI Automator dentro del bloque uiAutomator { ... }
. 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()
}
Cómo encontrar 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 encuentra 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 { id == "my_button_id" }.click() // Allow a permission request watchFor(PermissionDialog) { clickAllow() }
onElementOrNull { predicate }
: Similar aonElement
, pero devuelvenull
si la función no encuentra ningún elemento coincidente dentro del tiempo de espera. No arroja una excepción. Usa este método para los elementos opcionales.val optionalButton = onElementOrNull { textAsString() == "Skip" } optionalButton?.click() // Click only if the button exists
onElements { predicate }
: Espera hasta que al menos un elemento de la IU coincida con el predicado determinado y, luego, devuelve una lista de todos los elementos de la IU que coinciden.// 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 las llamadas a onElement
:
Encadena llamadas a
onElement
para elementos anidados: Puedes encadenar llamadas aonElement
para encontrar elementos dentro de otros elementos, siguiendo una jerarquía de elementos principales 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 { id == "first" } .onElement { id == "second" } .onElement { id == "third" } .click()
Especifica un tiempo de espera para las funciones
onElement*
pasando un valor que represente milisegundos.// Find a Ui element with a zero timeout (instant check) onElement(0) { id == "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
Interactúa con elementos de la IU simulando clics o configurando 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()
Cómo controlar los estados y los 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 que se está probando:
// 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")
Cómo controlar la IU inesperada
La API de watchFor
te permite definir controladores para elementos de IU inesperados, como diálogos de permisos, que pueden aparecer durante el flujo de prueba. Este usa el mecanismo de supervisión 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 a que la app esté visible y estable
A veces, las pruebas deben esperar a que los elementos se vuelvan visibles o estables. UI Automator ofrece varias APIs para ayudarte con esto.
El waitForAppToBeVisible("com.example.targetapp")
espera a que aparezca en la pantalla un elemento de 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 { id == "my_loading_indicator" }.waitForStable()
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 especialmente ú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 de IU individuales 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 { id == "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
ResultsReporter
te ayuda a asociar artefactos de prueba, como capturas de pantalla, directamente con los resultados de las pruebas 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 | Nuevo método de UI Automator |
---|---|---|
Punto de entrada | UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) |
Encapsula la lógica de prueba en el alcance de uiAutomator { ... } . |
Cómo encontrar elementos de la IU | device.findObject(By.res("com.example.app:id/my_button")) |
onElement { id == "my\_button" } |
Cómo encontrar elementos de la IU | device.findObject(By.text("Click Me")) |
onElement { textAsString() == "Click Me" } |
Espera a que la IU esté inactiva | device.waitForIdle() |
Prefiere el mecanismo de tiempo de espera integrado de onElement ; de lo contrario, activeWindow().waitForStable() |
Cómo encontrar elementos secundarios | Llamadas findObject anidadas manualmente |
Encadenamiento de onElement().onElement() |
Cómo controlar los diálogos de permisos | UiAutomator.registerWatcher() |
watchFor(PermissionDialog) |