UI Automator è un framework di Google per il test UI automatizzato di applicazioni Android che opera a livello di sistema e può interagire con elementi dell’interfaccia al di là di una singola applicazione. A differenza di Espresso, UI Automator non è legato al processo di un’applicazione specifica: può aprire finestre di dialogo di sistema, il pannello delle notifiche e passare da un’applicazione all’altra. Secondo Google Android Developers, UI Automator utilizza il Accessibility Service standard per accedere all’albero UI del dispositivo.
Punti chiave
UI Automator è un framework per il test funzionale dell’interfaccia utente di Android che opera a livello di sistema operativo. Fornisce un’API per accedere a qualsiasi elemento sullo schermo del dispositivo, indipendentemente dall’applicazione a cui appartiene — inclusa la barra di stato del sistema, le finestre di dialogo dei permessi, la schermata iniziale e le applicazioni di terze parti. Questo lo rende indispensabile per testare scenari che vanno oltre una singola applicazione.
Architetturalmente, UI Automator utilizza l’Accessibility Service — lo stesso servizio usato da TalkBack, Switch Access e altri strumenti di accessibilità. Attraverso questo servizio, il framework ottiene l’albero completo dei componenti UI della schermata corrente e consente di eseguire azioni su di essi: tocco, scorrimento, immissione di testo e pressione prolungata.
UI Automator è apparso per la prima volta in Android 4.3 (API 18) e da allora fa parte di Android Testing Support Library come strumento ufficiale di Google per i test tra applicazioni. In AndroidX Test è disponibile come artefatto separato androidx.test.uiautomator:uiautomator versione 2.3.0 (2024), che supporta tutte le versioni di Android dall’API 18.
Principio di funzionamento: UI Automator si basa sulla scansione dell’albero di accessibilità della schermata corrente. Quando viene chiamato il metodo findObject(selector), il framework attraversa la gerarchia delle View, trova il primo elemento che corrisponde alle condizioni di UiSelector e restituisce un UiObject — un proxy per interagire con la View reale.
Un tipico test UI Automator inizia ottenendo un’istanza di UiDevice, che rappresenta il dispositivo fisico. UiDevice fornisce metodi per trovare elementi, gestire la pressione dei pulsanti (Home, Indietro, Recenti), ruotare lo schermo e acquisire screenshot. Dopo aver trovato un elemento tramite UiSelector, vengono eseguite azioni sull’UiObject.
Nell’esempio seguente, il test apre l’app Impostazioni, trova l’elemento “Batteria” dal testo e lo tocca. UI Automator non richiede l’avvio di un’Activity — funziona con qualsiasi schermo del dispositivo, incluse le applicazioni di terze parti.
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
// Aprire la schermata delle impostazioni
device.pressHome()
device.wait(Until.hasObject(UiSelector().text("Impostazioni")), 2000)
// Trovare l'elemento "Batteria" e toccare
val batteryItem = device.findObject(
UiSelector().text("Batteria")
)
batteryItem.clickAndWait(Until.newWindow(), 3000)
UiDevice è la classe principale per interagire con il dispositivo. Fornisce metodi per trovare elementi, simulare la pressione dei pulsanti hardware (Home, Indietro, Menu, Volume), gestire l’alimentazione, acquisire screenshot e attendere stati specifici dello schermo. UiDevice viene creato una volta per test e riutilizzato per tutte le operazioni.
UiSelector è un’API fluida per trovare elementi UI. A differenza di Espresso ViewMatchers, UiSelector non richiede compilazione — le condizioni di ricerca sono formate attraverso una catena di metodi: text(), className(), description(), resourceId(), index(). Più condizioni vengono combinate automaticamente tramite AND logico.
| Metodo UiSelector | Scopo |
|---|---|
| text(String) | Ricerca per testo esatto dell’elemento |
| textContains(String) | Ricerca per corrispondenza parziale del testo |
| resourceId(String) | Ricerca per ID risorsa (es. com.example:id/button) |
| className(String) | Ricerca per nome classe della View |
| description(String) | Ricerca per content-description |
| childSelector(selector) | Ricerca di un elemento figlio all’interno di un contenitore |
Quando più elementi sullo schermo condividono lo stesso testo, UiSelector consente di combinare criteri: trovare un contenitore per ID, poi al suo interno — un elemento per testo e classe. Ciò garantisce l’identificazione univoca del componente desiderato. Il metodo childSelector restringe l’ambito di ricerca a un contenitore specifico, accelerando la navigazione nell’albero UI.
val scrollView = device.findObject(
UiSelector().resourceId("android:id/list")
)
// All'interno dell'elenco, trovare l'elemento con testo "Wi-Fi"
val wifiItem = scrollView.findObject(
UiSelector().text("Wi-Fi")
wifiItem.click()
I test tra applicazioni (cross-application) sono la funzionalità principale per cui UI Automator viene scelto. Il framework può passare da un’app all’altra, testare il login OAuth tramite browser, verificare le finestre di dialogo di sistema (permessi, selettore app) e interagire con la barra di stato del sistema, il pannello delle notifiche e la schermata di blocco.
Uno scenario tipico di test cross-app: l’app apre un browser per l’autorizzazione OAuth, l’utente inserisce il proprio nome utente e password, e il browser reindirizza all’app. UI Automator passa da un processo all’altro, trova i campi di input nel browser, li compila e tocca “Accedi”.
// Attesa della comparsa del browser
device.wait(Until.hasObject(
UiSelector().packageName("com.android.chrome")
), 5000)
// Ricerca del campo di inserimento email nel browser
val emailField = device.findObject(
UiSelector().className("android.widget.EditText").instance(0)
)
emailField.text = "user@example.com"
UI Automator può verificare e chiudere le finestre di dialogo di sistema — permessi di localizzazione, notifiche, accesso ai file. Questo è criticamente importante per testare scenari di primo avvio quando il sistema richiede sequenzialmente più permessi. Senza UI Automator, questi scenari non possono essere automatizzati poiché le finestre di dialogo di sistema non appartengono al processo dell’app.
La scelta tra UI Automator e Espresso dipende dallo scenario di test. Espresso è ottimizzato per testare una singola applicazione con sincronizzazione automatica e boilerplate minimo. UI Automator è adatto per scenari in cui è necessario interagire con il sistema, il browser o più applicazioni.
| Criterio | UI Automator | Espresso |
|---|---|---|
| Ambito | Intero dispositivo, più applicazioni | Singola applicazione |
| Sincronizzazione | Manuale (attesa, pausa) | Automatica (Idling Resource) |
| Velocità | Più lento (accesso tramite servizio) | Più veloce (funziona all’interno del processo) |
| UI di sistema | Supporta (Notifiche, Impostazioni rapide) | Non supporta |
| Precisione di ricerca | UiSelector per attributi | ViewMatchers per tipo e gerarchia |
| Stabilità | Inferiore (dipende dai tempi) | Superiore (attesa automatica) |
In pratica, questi framework sono spesso usati insieme: Espresso copre i test UI dell’app principale con elevata stabilità, mentre UI Automator viene utilizzato per scenari che vanno oltre i confini dell’app — login OAuth, permessi di sistema, lavoro con Share Intent. Questa combinazione offre la massima copertura UI con costi di manutenzione dei test minimi.
L’integrazione di UI Automator si effettua aggiungendo una dipendenza in build.gradle. Il framework fa parte di AndroidX Test e non richiede permessi aggiuntivi nel manifest — l’accesso all’Accessibility Service viene configurato automaticamente all’avvio del test strumentato.
La configurazione minima include l’artefatto uiautomator e l’esecutore di test standard AndroidJUnitRunner. I test UI Automator sono inseriti nella directory src/androidTest e vengono eseguiti su un emulatore o dispositivo fisico con Android API 18+.
dependencies {
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.3.0")
androidTestImplementation("androidx.test.ext:junit:1.2.1")
androidTestImplementation("androidx.test:runner:1.6.1")
}
Per ottenere un’istanza di UiDevice, si usa InstrumentationRegistry.getInstrumentation(). UiDevice dovrebbe essere creato una volta nel metodo setUp() e riutilizzato in tutti i test della classe per conservare le risorse del dispositivo. È importante notare che UiDevice non è thread-safe — tutte le operazioni devono essere eseguite nello stesso thread del metodo di test. Creare un nuovo UiDevice in ogni test genera overhead e rallenta l’esecuzione. Si consiglia di creare UiDevice una volta nel metodo beforeClass e riutilizzarlo per tutti i test della classe di test.
A differenza di Espresso, UI Automator non ha sincronizzazione automatica. Per attendere la comparsa degli elementi, si usa il metodo UiDevice.wait(condition, timeout) con un oggetto Until: Until.findObject(selector), Until.hasObject(selector), Until.gone(selector). Senza attese adeguate, i test diventano instabili a causa di race condition — un elemento potrebbe non essere ancora apparso sullo schermo al momento della ricerca. Si consiglia di impostare un timeout di almeno 3–5 secondi per la stabilità.
Domande frequenti
UI Automator opera a livello di Accessibility Service e può interagire con qualsiasi applicazione. Espresso funziona all’interno del processo di una singola app e utilizza la sincronizzazione automatica con il thread UI. UI Automator è migliore per scenari tra applicazioni, mentre Espresso è migliore per test stabili di una singola applicazione.
Sì, UI Automator funziona su tutti i dispositivi con Android API 18+. Non richiede accesso root — utilizza l’Accessibility Service standard, che viene attivato tramite Instrumentation all’avvio dei test.
UI Automator utilizza l’Accessibility Service per ottenere l’albero completo dei componenti UI della schermata corrente. Quindi UiSelector attraversa questo albero e trova gli elementi in base ai criteri specificati: testo, classe, ID, content-description o una loro combinazione.
Sì, il metodo UiDevice.takeScreenshot(storePath) consente di acquisire uno screenshot della schermata corrente e salvarlo in un file. Ciò è utile per il debug: quando un test fallisce, è possibile salvare lo screenshot e analizzare lo stato della schermata.
UI Automator non ha sincronizzazione automatica, quindi i test sono sensibili ai tempi. Se un’animazione non è terminata o una View non è stata ancora renderizzata, findObject potrebbe non trovare l’elemento. La soluzione è usare UiDevice.wait() con un timeout sufficiente.
Riepilogo
La suite di strumenti UI Automator copre tutti gli scenari chiave di test tra applicazioni ed è lo standard per l’automazione Android a livello di sistema.
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche