Il framework di test UI Automator fornisce un insieme di API per creare test dell'interfaccia utente che interagiscono con le app utente e di sistema.
Introduzione ai test moderni di UI Automator
UI Automator 2.4 introduce un linguaggio specifico del dominio (DSL) semplificato e compatibile con Kotlin che semplifica la scrittura di test UI per Android. Questa nuova superficie API si concentra sulla ricerca di elementi basata su predicati e sul controllo esplicito degli stati dell'app. Utilizzalo per creare test automatizzati più gestibili e affidabili.
UI Automator ti consente di testare un'app dall'esterno del processo dell'app. In questo modo puoi testare le versioni di release con la minimizzazione applicata. UI Automator è utile anche per scrivere test macrobenchmark.
Le caratteristiche principali dell'approccio moderno includono:
- Un ambito di test
uiAutomator
dedicato per un codice di test più pulito ed espressivo. - Metodi come
onElement
,onElements
eonElementOrNull
per trovare elementi della UI con predicati chiari. - Meccanismo di attesa integrato per gli elementi condizionali
onElement*(timeoutMs: Long = 10000)
- Gestione esplicita dello stato dell'app, ad esempio
waitForStable
ewaitForAppToBeVisible
. - Interazione diretta con i nodi della finestra di accessibilità per scenari di test multi-finestra.
- Funzionalità di screenshot integrate e un
ResultsReporter
per test visivi e debug.
Configura il progetto
Per iniziare a utilizzare le moderne API UI Automator, aggiorna il file
build.gradle.kts
del tuo progetto in modo da includere la dipendenza più recente:
Kotlin
dependencies {
...
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.4.0-alpha05")
}
Groovy
dependencies {
...
androidTestImplementation "androidx.test.uiautomator:uiautomator:2.4.0-alpha05"
}
Concetti principali delle API
Le sezioni seguenti descrivono i concetti di base della moderna API UI Automator.
Ambito del test uiAutomator
Accedi a tutte le nuove API UI Automator all'interno del blocco uiAutomator { ... }
. Questa funzione crea un UiAutomatorTestScope
che fornisce un ambiente conciso e type-safe per le operazioni di test.
uiAutomator {
// All your UI Automator actions go here
startApp("com.example.targetapp")
onElement { textAsString() == "Hello, World!" }.click()
}
Trovare elementi UI
Utilizza le API UI Automator con i predicati per individuare gli elementi dell'interfaccia utente. Questi predicati ti consentono di definire le condizioni per proprietà come testo, stato selezionato o attivo e descrizione dei contenuti.
onElement { predicate }
: restituisce il primo elemento UI che corrisponde al predicato entro un timeout predefinito. La funzione genera un'eccezione se non trova un elemento corrispondente.// 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 }
: Simile aonElement
, ma restituiscenull
se la funzione non trova alcun elemento corrispondente entro il timeout. Non genera un'eccezione. Utilizza questo metodo per gli elementi facoltativi.val optionalButton = onElementOrNull { textAsString() == "Skip" } optionalButton?.click() // Click only if the button exists
onElements { predicate }
: attende che almeno un elemento UI corrisponda al predicato specificato, quindi restituisce un elenco di tutti gli elementi UI corrispondenti.// Get all items in a list Ui element val listItems = onElements { className == "android.widget.TextView" && isClickable } listItems.forEach { it.click() }
Ecco alcuni suggerimenti per l'utilizzo delle chiamate onElement
:
Concatenare chiamate
onElement
per elementi nidificati: puoi concatenare chiamateonElement
per trovare elementi all'interno di altri elementi, seguendo una gerarchia padre-figlio.// 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()
Specifica un timeout per le funzioni
onElement*
passando un valore che rappresenta i millisecondi.// 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()
Interagire con gli elementi dell'interfaccia utente
Interagisci con gli elementi UI simulando i clic o impostando il testo nei campi modificabili.
// 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()
Gestire gli stati e i watcher delle app
Gestisci il ciclo di vita della tua app e gestisci gli elementi UI imprevisti che potrebbero apparire durante i test.
Gestione del ciclo di vita dell'app
Le API forniscono modi per controllare lo stato dell'app in fase di test:
// 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")
Gestire l'interfaccia utente imprevista
L'API watchFor
ti consente di definire i gestori per elementi UI imprevisti,
come le finestre di dialogo delle autorizzazioni, che potrebbero apparire durante il flusso di test. Questo
utilizza il meccanismo di monitoraggio interno, ma offre maggiore flessibilità.
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
è un esempio di ScopedWatcher<T>
, dove T
è l'oggetto passato come ambito al blocco in watchFor
. Puoi creare watcher personalizzati in base a questo pattern.
Attendi la visibilità e la stabilità dell'app
A volte i test devono attendere che gli elementi diventino visibili o stabili. UI Automator offre diverse API per aiutarti in questo.
waitForAppToBeVisible("com.example.targetapp")
attende che sullo schermo venga visualizzato un elemento UI con il nome del pacchetto specificato entro un timeout personalizzabile.
// Wait for the app to be visible after launching it
startApp("com.example.targetapp")
waitForAppToBeVisible("com.example.targetapp")
Utilizza l'API waitForStable()
per verificare che l'interfaccia utente dell'app sia considerata stabile
prima di interagire con essa.
// 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()
Funzionalità avanzate
Le seguenti funzionalità sono utili per scenari di test più complessi.
Interagire con più finestre
Le API UI Automator ti consentono di interagire direttamente con gli elementi della UI e di esaminarli. Ciò è particolarmente utile per gli scenari che coinvolgono più finestre, come la modalità Picture in picture (PIP) o i layout a schermo diviso.
// 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()
Screenshot e asserzioni visive
Acquisisci screenshot dell'intero schermo, di finestre specifiche o di singoli elementi dell'interfaccia utente direttamente nei test. Questo è utile per il test e il debug della regressione visiva.
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 funzione di estensione saveToFile
per Bitmap semplifica il salvataggio dell'immagine acquisita in un percorso specificato.
Utilizzare ResultsReporter per il debug
ResultsReporter
ti aiuta ad associare gli artefatti di test, come gli screenshot,
direttamente ai risultati dei test in Android Studio per semplificare l'ispezione e
il debug.
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()
}
Eseguire la migrazione da versioni precedenti di UI Automator
Se hai test UI Automator esistenti scritti con interfacce API precedenti, utilizza la seguente tabella come riferimento per la migrazione all'approccio moderno:
Tipo di azione | Vecchio metodo UI Automator | Nuovo metodo UI Automator |
---|---|---|
Punto di ingresso | UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) |
Inserisci la logica di test nell'ambito uiAutomator { ... } . |
Trovare elementi UI | device.findObject(By.res("com.example.app:id/my_button")) |
onElement { id == "my\_button" } |
Trovare elementi UI | device.findObject(By.text("Click Me")) |
onElement { textAsString() == "Click Me" } |
Attendi UI inattiva | device.waitForIdle() |
Preferisci il meccanismo di timeout integrato di onElement ; altrimenti, activeWindow().waitForStable() |
Trovare gli elementi secondari | Chiamate findObject nidificate manualmente |
onElement().onElement() concatenamento |
Gestire le finestre di dialogo delle autorizzazioni | UiAutomator.registerWatcher() |
watchFor(PermissionDialog) |