I test dell'interfaccia utente verificano la correttezza della visualizzazione e dell'interazione degli elementi dell'interfaccia utente di un'app mobile — pulsanti, campi di testo, elenchi e componenti di navigazione. A differenza dei test unitari che verificano la logica di business, i test UI simulano le azioni dell'utente: tocchi, scorrimenti, inserimento di testo e verificano la risposta dell'interfaccia. Secondo uno studio di Android Developers, 2024, i test UI coprono il 70% degli scenari critici dell'utente e consentono di individuare difetti di layout non rilevabili tramite controlli logici.
Punti chiave
I test UI sono un tipo di verifica automatizzata in cui il codice di test interagisce con l'interfaccia grafica dell'applicazione esattamente come farebbe un utente reale. Il test trova un elemento sullo schermo — un pulsante, un campo di testo, un elenco — esegue un'azione su di esso e verifica la risposta prevista dell'interfaccia. Ad esempio, dopo aver inserito una password errata, un test UI verifica che sullo schermo appaia un messaggio di errore con il testo corretto.
La differenza principale tra i test UI e altri tipi di automazione è che funzionano attraverso il livello di accessibilità del sistema operativo, non attraverso le API interne dell'applicazione. Ciò significa che i test UI vedono l'interfaccia esattamente come la vedrebbero un utente e un lettore di schermo. Grazie a ciò, i test UI verificano non solo la funzionalità ma anche l'accessibilità degli elementi — la conformità ai requisiti WCAG.
Secondo il sondaggio JetBrains Developer Ecosystem 2023, il 58% dei team mobile utilizza test UI nella propria pipeline CI/CD. La copertura media dei test UI nei progetti commerciali è del 30–40% degli schermi dell'applicazione. I progetti con test UI ricevono il 25% in meno di recensioni negative negli app store relative a crash dell'interfaccia.
La differenza principale tra test UI e test unitari è il livello di astrazione. I test unitari lavorano con classi e funzioni individuali, isolate dal framework Android o iOS. Vengono eseguiti sulla JVM (per Android) senza avviare un emulatore e richiedono millisecondi. I test UI vengono eseguiti su un dispositivo reale o emulatore, interagiscono con i servizi di sistema e richiedono secondi o minuti per scenario.
Anche il pubblico target dei test è diverso. I test UI verificano scenari utente completi — registrazione, inserimento ordine, ricerca. I test unitari coprono la logica di business: calcoli, validazione, trasformazione dei dati. Un test UI non verifica la correttezza del calcolo delle tasse — verifica che l'importo totale venga visualizzato sullo schermo. Il calcolo stesso è verificato da un test unitario.
Secondo il Google Testing Blog (2020), il rapporto ottimale dei test in un progetto segue la regola della piramide di test: 70% test unitari, 20% test di integrazione e 10% test UI. La violazione di questa proporzione a favore dei test UI porta a un aumento del tempo di esecuzione e alla fragilità della suite di test, poiché i test UI sono sensibili ai cambiamenti nel layout degli schermi.
Per Android, il framework dominante è Espresso — una libreria di Google integrata in AndroidX Test. Espresso si sincronizza automaticamente con il thread UI, attendendo il completamento di animazioni e attività in background prima di eseguire la successiva verifica. Per Jetpack Compose, viene utilizzata l'estensione Compose UI Test, che funziona tramite nodi semantici invece degli identificatori di vista tradizionali.
Per iOS, lo strumento principale è XCUITest, che fa parte di Xcode. I test sono scritti in Swift e utilizzano identificatori di accessibilità per trovare gli elementi. XCUITest supporta la registrazione di test tramite la funzione di registrazione e l'integrazione con sistemi CI tramite xcodebuild. Per progetti multipiattaforma, viene utilizzato Appium, basato sul protocollo WebDriver e che consente di eseguire gli stessi test su Android e iOS con modifiche minime al codice.
Espresso funziona con il sistema di viste tradizionale tramite onView e identificatori di risorse. Compose UI Test utilizza un livello semantico, rendendo i test meno dipendenti dalla gerarchia delle viste. Ad esempio, la ricerca di un pulsante in Espresso: onView(withId(R.id.submit)), in Compose: onNodeWithTag(“submit”). I test Compose gestiscono automaticamente la ricomposizione e non richiedono attese esplicite dello stato di inattività.
XCUITest utilizza XCUIApplication come punto di ingresso. Ogni elemento dell'interfaccia viene trovato tramite proprietà di accessibilità: accessibilityIdentifier per l'accesso programmatico e accessibilityLabel per VoiceOver. Il framework supporta la registrazione di test tramite la funzione di registrazione di Xcode — lo sviluppatore esegue azioni sul simulatore e Xcode genera il codice di test. I test pronti vengono eseguiti tramite xcodebuild test.
Appium è basato sul protocollo WebDriver e supporta qualsiasi linguaggio: Java, Python, JavaScript. Le strategie di ricerca degli elementi includono id, xpath, class name e accessibility id. Appium richiede l'installazione del server e la configurazione di Desired Capabilities — platformName, deviceName, appPackage. Un'alternativa è Maestro, che utilizza scenari YAML e non richiede la compilazione del codice di test.
Consideriamo i test UI per lo stesso scenario — login nell'applicazione — su tre framework diversi: Espresso per Android, XCUITest per iOS e Appium per l'approccio multipiattaforma. Scenario: inserire login e password, premere il pulsante di login, verificare la visualizzazione del messaggio di benvenuto.
Un test Espresso utilizza onView per trovare un elemento tramite il suo identificatore e perform per eseguire un'azione. Il metodo check con il matcher isDisplayed conferma che l'elemento è visibile sullo schermo.
@RunWith(AndroidJUnit4::class)
class LoginUiTest {
@Rule
@JvmField
val composeTestRule = createComposeRule()
@Test
fun login_withValidCredentials_showsWelcome() {
composeTestRule
.onNodeWithTag("emailField")
.performTextInput("user@example.com")
composeTestRule
.onNodeWithTag("passwordField")
.performTextInput("secret123")
composeTestRule
.onNodeWithTag("loginButton")
.performClick()
composeTestRule
.onNodeWithText("Benvenuto, Utente!")
.assertIsDisplayed()
}
}
XCUITest utilizza XCUIApplication per accedere agli elementi dell'interfaccia tramite identificatori di accessibilità. I metodi tap() e exists forniscono interazione e verifica.
class LoginUITests: XCTestCase {
let app = XCUIApplication()
override func setUp() {
continueAfterFailure = false
app.launch()
}
func testLogin_withValidCredentials_showsWelcome() {
app.textFields["emailField"].tap()
app.textFields["emailField"].typeText("user@example.com")
app.secureTextFields["passwordField"].tap()
app.secureTextFields["passwordField"].typeText("secret123")
app.buttons["loginButton"].tap()
XCTAssertTrue(app.staticTexts["Welcome, User!"].exists)
}
}
Il primo principio — utilizza identificatori di accessibilità invece di etichette di testo per trovare gli elementi. Il testo di un pulsante può cambiare durante la localizzazione, mentre l'identificatore rimane stabile. In Android, questa è la proprietà contentDescription; in iOS — accessibilityIdentifier. Questo approccio rende i test indipendenti dalla lingua dell'interfaccia e riduce i costi di manutenzione quando cambia la copywriting.
Evita sleep() e ritardi fissi — utilizza i meccanismi di attesa integrati del framework. Espresso attende automaticamente il completamento di animazioni e attività in background. XCUITest fornisce XCTAssertTrue con timeout. Le pause esplicite rendono i test più lenti e instabili, specialmente su dispositivi lenti in ambiente CI.
Raggruppa i test per criticità: i test smoke (3–5 scenari chiave) vengono eseguiti a ogni commit, la suite completa di test UI viene eseguita prima del rilascio. Secondo il Google Testing Blog (2022), i test UI che richiedono più di 30 minuti in CI riducono la frequenza di esecuzione del 40%, diminuendo la loro efficacia come strumento di rilevamento precoce delle regressioni.
I test UI presentano diverse limitazioni. Sensibilità ai cambiamenti di layout: modificare un identificatore, una gerarchia o un tipo di elemento rompe il test anche se la funzionalità rimane invariata. La soluzione è utilizzare il pattern Page Object, che centralizza i selettori di elementi in classi separate. Quando il layout cambia, viene corretto un solo file Page Object, non decine di test.
Tempo di esecuzione: l'esecuzione su un dispositivo reale o emulatore richiede da 10 a 50 volte più tempo di un test unitario. La soluzione è eseguire i test UI in parallelo su più dispositivi tramite Firebase Test Lab o AWS Device Farm. L'instabilità (flakiness) è un problema comune delle esecuzioni CI, causato da animazioni, ritardi di rete o stato dell'emulatore. Per combattere l'instabilità, vengono utilizzati tentativi automatici di test falliti e analisi di stabilità di ogni scenario di test.
Domande frequenti
Per uno schermo medio, 3–5 test UI sono sufficienti: happy path, validazione errori, stato vuoto, cambio di orientamento e verifica di accessibilità. Schermi complessi con più stati — moduli d'ordine, impostazioni — possono richiedere 10–15 test per una copertura completa degli scenari chiave.
Sì, Appium e Maestro consentono di eseguire gli stessi scenari su entrambe le piattaforme. Tuttavia, i framework nativi — Espresso e XCUITest — offrono migliore stabilità, velocità e accesso a funzionalità specifiche della piattaforma non disponibili tramite proxy WebDriver.
Per Compose, viene utilizzata la libreria Compose UI Test con matcher semantici: onNodeWithText, onNodeWithTag, onNodeWithContentDescription. Il livello semantico di Compose astrae la gerarchia delle viste, rendendo i test meno fragili rispetto a Espresso tradizionale per il sistema di viste.
L'esecuzione di base dei test UI viene effettuata su emulatori in CI — è veloce ed economica. La verifica finale prima del rilascio dovrebbe essere effettuata su dispositivi fisici tramite Firebase Test Lab per considerare le caratteristiche dell'hardware reale: diverse risoluzioni, versioni del SO e prestazioni.
Utilizza l'esecuzione parallela su più dispositivi, disabilita le animazioni sull'emulatore tramite le Opzioni sviluppatore, costruisci un'architettura di test modulare ed esegui la suite smoke a ogni commit, con l'esecuzione di regressione completa pianificata o prima del rilascio.
Riepilogo
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