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 UI Automator moderni
UI Automator 2.4 introduce un linguaggio specifico del dominio (DSL) semplificato e compatibile con Kotlin che semplifica la scrittura dei test dell'interfaccia utente per Android. Questa nuova superficie API si concentra sulla ricerca di elementi basata su predicati e sul controllo esplicito degli stati delle app. Utilizzala per creare test automatici 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 minificazione applicata. UI Automator è utile anche per scrivere test di macrobenchmark.
Le funzionalità principali dell'approccio moderno includono:
- Un ambito di test
uiAutomatordedicato per un codice di test più pulito ed espressivo. - Metodi come
onElement,onElementseonElementOrNullper trovare elementi dell'interfaccia utente con predicati chiari. - Meccanismo di attesa integrato per gli elementi condizionali
onElement*(timeoutMs: Long = 10000) - Gestione esplicita dello stato dell'app, ad esempio
waitForStableewaitForAppToBeVisible. - Interazione diretta con i nodi della finestra di accessibilità per scenari di test multi-finestra
- Funzionalità di screenshot integrate e un
ResultsReporterper test visivi e debug.
Configura il progetto
Per iniziare a utilizzare le API UI Automator moderne, aggiorna il file
build.gradle.kts del 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 di base dell'API
Le sezioni seguenti descrivono i concetti di base dell'API UI Automator moderna.
L'ambito di test uiAutomator
Accedi a tutte le nuove API UI Automator all'interno del uiAutomator { ... }
blocco. Questa funzione crea un UiAutomatorTestScope che fornisce un ambiente conciso
e con sicurezza dei tipi per le operazioni di test.
uiAutomator {
// All your UI Automator actions go here
startApp("com.example.targetapp")
onElement { textAsString() == "Hello, World!" }.click()
}
Trova elementi dell'interfaccia utente
Utilizza le API UI Automator con i predicati per individuare gli elementi dell'interfaccia utente. Questi predicati ti consentono di definire le condizioni per le proprietà come testo, stato selezionato o con stato attivo e descrizione dei contenuti.
onElement { predicate }: restituisce il primo elemento dell'interfaccia utente che corrisponde al predicato entro un timeout predefinito. La funzione genera un'eccezione se non individua 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 { viewIdResourceName == "my_button_id" }.click() // Allow a permission request watchFor(PermissionDialog) { clickAllow() }onElementOrNull { predicate }: simile aonElement, ma restituiscenullse la funzione non trova un 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 existsonElements { predicate }: attende che almeno un elemento dell'interfaccia utente corrisponda a l predicato specificato, quindi restituisce un elenco di tutti gli elementi dell'interfaccia utente 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:
Concatenamento delle chiamate
onElementper gli elementi nidificati: puoi concatenare le chiamateonElementper 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 { viewIdResourceName == "first" } .onElement { viewIdResourceName == "second" } .onElement { viewIdResourceName == "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) { viewIdResourceName == "something" }.click() // Find a Ui element with a custom timeout of 10 seconds onElement(10_000) { textAsString() == "Long loading text" }.click()
Interagisci con gli elementi dell'interfaccia utente
Interagisci con gli elementi dell'interfaccia utente 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()
Gestisci gli stati e i watcher delle app
Gestisci il ciclo di vita dell'app e gestisci gli elementi dell'interfaccia utente imprevisti che potrebbero apparire durante i test.
Gestione del ciclo di vita delle 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")
Gestisci l'interfaccia utente imprevista
L'API watchFor ti consente di definire i gestori per gli elementi dell'interfaccia utente imprevisti,
ad esempio le finestre di dialogo di autorizzazione, che potrebbero apparire durante il flusso di test. Utilizza il meccanismo di watcher 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.
Il waitForAppToBeVisible("com.example.targetapp") attende che un elemento dell'interfaccia utente con
il nome del pacchetto specificato venga visualizzato sullo schermo 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 { viewIdResourceName == "my_loading_indicator" }.waitForStable()
Utilizza UI Automator per macrobenchmark e profili di base
Utilizza UI Automator per i test delle prestazioni con Jetpack Macrobenchmark e per la generazione di profili di base, in quanto fornisce un modo affidabile per interagire con l'app e misurare le prestazioni dal punto di vista dell'utente finale.
Macrobenchmark utilizza le API UI Automator per gestire l'interfaccia utente e misurare le interazioni.
Ad esempio, nei benchmark di avvio, puoi utilizzare onElement per rilevare quando i contenuti dell'interfaccia utente sono completamente caricati, consentendoti di misurare il tempo di visualizzazione completa
(TTFD). Nei benchmark di jank, le API UI Automator vengono utilizzate per scorrere gli elenchi o
eseguire animazioni per misurare i tempi dei frame. Funzioni come startActivity() o
startIntent() sono utili per portare l'app nello stato corretto prima dell'
inizio della misurazione.
Quando generi i profili di base, automatizzi i percorsi utente critici
della tua app (CUJ) per registrare le classi e i metodi che richiedono la precompilazione. UI
Automator è uno strumento ideale per scrivere questi script di automazione. La ricerca di elementi basata su predicati e i meccanismi di attesa integrati (onElement)
del DSL moderno
portano a un'esecuzione dei test più robusta e deterministica rispetto ad altri metodi.
Questa stabilità riduce la variabilità e garantisce che il profilo di base generato
rifletta accuratamente i percorsi del codice eseguiti durante i flussi utente più importanti.
Funzionalità avanzate
Le seguenti funzionalità sono utili per scenari di test più complessi.
Interagisci con più finestre
Le API UI Automator ti consentono di interagire direttamente con gli elementi dell'interfaccia utente e di esaminarli. Questo è 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 all'interno dei test. Questo è utile per i test di regressione visiva e il debug.
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 funzione di estensione saveToFile per Bitmap semplifica il salvataggio dell'immagine acquisita
in un percorso specificato.
Utilizza 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()
}
Esegui la migrazione dalle versioni precedenti di UI Automator
Se hai test UI Automator esistenti scritti con superfici API precedenti, utilizza la seguente tabella come riferimento per eseguire la migrazione all'approccio moderno:
| Tipo di azione | Metodo UI Automator precedente | Nuovo metodo UI Automator |
|---|---|---|
| Punto di ingresso | UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) |
Esegui il wrapping della logica di test nell'ambito uiAutomator { ... }. |
| Trova elementi dell'interfaccia utente | device.findObject(By.res("com.example.app:id/my_button")) |
onElement { viewIdResourceName == "my\_button" } |
| Trova elementi dell'interfaccia utente | device.findObject(By.text("Click Me")) |
onElement { textAsString() == "Click Me" } |
| Attendi l'interfaccia utente inattiva | device.waitForIdle() |
Preferisci il meccanismo di timeout integrato di onElement; in caso contrario, activeWindow().waitForStable() |
| Trova elementi secondari | Chiamate findObject nidificate manualmente |
Concatenamento onElement().onElement() |
| Gestisci le finestre di dialogo di autorizzazione | UiAutomator.registerWatcher() |
watchFor(PermissionDialog) |