UI Automator: Tests jenseits der eigenen App
UI Automator ermöglicht Tests über App-Grenzen hinweg. Du lernst, System-UI und andere Apps in Testszenarien einzubeziehen.
UI Automator gehört zu den Testwerkzeugen, die du genau dann brauchst, wenn Espresso an seine Grenzen stößt: sobald dein Test die Grenzen der eigenen App verlässt und mit System-UI oder einer fremden App interagieren muss. Berechtigungsdialoge annehmen, die Notification-Leiste öffnen, in den Einstellungen navigieren – all das ist mit Espresso allein nicht möglich. Wer qualitativ hochwertige Apps ausliefern will, kommt um dieses Wissen nicht herum.
Was ist das?
UI Automator ist ein Testframework aus der AndroidX Test Library, das dir erlaubt, jedes sichtbare UI-Element auf dem Gerät anzusteuern – unabhängig davon, zu welcher App es gehört. Während Espresso-Tests innerhalb des Prozesses deiner eigenen App laufen, läuft UI Automator als separater Prozess und kommuniziert über den Accessibility-Service des Betriebssystems mit der gesamten Geräteoberfläche.
Das bedeutet konkret: Du kannst den System-Back-Button oder den Home-Button programmatisch drücken, Benachrichtigungen in der Notification-Shade lesen und antippen, Berechtigungsdialoge des Systems automatisch bestätigen oder ablehnen, in den Android-Einstellungen navigieren und sogar eine komplett andere App starten und mit ihr interagieren.
Für einen Lernenden auf dem Weg zum Senior-Entwickler ist das entscheidend: Viele reale Nutzerflows überschreiten App-Grenzen. Ein Bezahlvorgang öffnet eine Browser-Seite oder eine Wallet-App. Kamera-Permission muss beim ersten App-Start bestätigt werden. Ein Push-Tap bringt den Nutzer aus einer fremden App zurück. Solche Szenarien lassen sich nur mit einem Werkzeug wie UI Automator vollständig automatisieren – und genau das ist es, was die Android-Testing-Grundlagen unter End-to-End-Tests verstehen.
Wie funktioniert es?
Der Einstiegspunkt ist UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()). Dieses Singleton repräsentiert das physische Gerät und stellt Methoden wie pressHome(), pressBack(), openNotification() oder swipe() bereit.
Einzelne UI-Elemente suchst du als UiObject2-Instanzen über By-Selektoren:
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
// Warte bis zu 5 Sekunden auf den Berechtigungsdialog
device.wait(Until.hasObject(By.text("Zulassen")), 5_000)
val allowButton = device.findObject(By.text("Zulassen"))
allowButton?.click()
By bietet Selektoren nach text, contentDesc, res (Resource-ID), pkg (Package-Name) und weiteren Attributen. Until stellt Bedingungen für explizite Wartezeiten bereit – sie sind fast immer notwendig, weil System-Animationen und App-Starts asynchron ablaufen.
Für die Projekteinrichtung benötigst du in build.gradle.kts folgende Testabhängigkeiten:
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.3.0")
androidTestImplementation("androidx.test:runner:1.6.2")
androidTestImplementation("androidx.test:rules:1.6.0")
UI-Automator-Tests laufen als Instrumented Tests im androidTest-Ordner und benötigen ein physisches Gerät oder einen Emulator.
In der Praxis
Ein klassischer Anwendungsfall ist der Permission-Flow beim ersten App-Start. Viele Apps fordern Berechtigungen genau dann an, wenn ein bestimmter Screen zum ersten Mal geöffnet wird – ohne diesen Schritt zu automatisieren, schlägt jeder nachfolgende Test fehl.
@RunWith(AndroidJUnit4::class)
class CameraPermissionTest {
private val device = UiDevice.getInstance(
InstrumentationRegistry.getInstrumentation()
)
@Test
fun startet_kamera_nach_berechtigungserteilung() {
val context = ApplicationProvider.getApplicationContext<Context>()
val intent = context.packageManager
.getLaunchIntentForPackage(context.packageName)
?.apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) }
context.startActivity(intent)
// Warte auf Berechtigungsdialog
device.wait(Until.hasObject(By.text("Zulassen")), 5_000)
device.findObject(By.text("Zulassen"))?.click()
// Verifiziere, dass der Kamera-Screen erscheint
val preview = By.res(context.packageName, "camera_preview")
device.wait(Until.hasObject(preview), 5_000)
assertThat(device.findObject(preview)).isNotNull()
}
}
Typische Stolperfalle: Timing ohne explizite Wartezeiten
Ein häufiger Fehler ist, direkt nach einer Aktion mit findObject zu suchen, ohne auf das Erscheinen des Elements zu warten. System-Dialoge und App-Übergänge brauchen einen kurzen Moment. Verwende immer device.wait(Until.hasObject(...), timeout) bevor du findObject aufrufst. Thread.sleep als Alternative ist fragil und verlangsamt die Test-Suite unnötig.
Sprache der System-Texte beachten
Selektoren wie By.text("Zulassen") sind sprachabhängig. Auf einem englischsprachigen Gerät heißt der Button „Allow”. Robustere Alternativen sind Resource-IDs – etwa By.res("com.android.permissioncontroller", "permission_allow_button") – oder Content-Descriptions. Prüfe die tatsächlichen IDs mit dem uiautomatorviewer-Tool oder dem Layout Inspector in Android Studio, bevor du Texte als Selektoren verwendest.
UI Automator und Espresso kombinieren
In der Praxis kombinieren Teams beide Frameworks: UI Automator übernimmt System-Interaktionen wie das Erteilen von Berechtigungen oder das Wegwischen von Benachrichtigungen, während Espresso danach den eigentlichen Feature-Flow innerhalb der App verifiziert. Dieses Muster hält die Tests wartbar und nutzt die Stärken beider Werkzeuge gemäß den Android-Qualitätsrichtlinien.
Fazit
UI Automator füllt die Lücke, die Espresso offenlässt: Cross-App-Flows, System-Dialoge und geräteweite Interaktionen. Versteh es nicht als Ersatz für Unit- oder Integrationstests, sondern als oberstes Stockwerk der Testpyramide – selektiv einzusetzen für die kritischsten End-to-End-Szenarien. Um dein Verständnis zu festigen, richte einen Test ein, der einen echten Permission-Flow in deiner eigenen App durchläuft. Beobachte dabei im Android Studio Logcat, wie der Accessibility-Service die Gerätesteuerbefehle weitergibt, und prüfe mit dem Layout Inspector, welche Resource-IDs die System-Dialoge auf dem Zielgerät tatsächlich tragen. Erst wer seinen Test auf einem englischen und einem deutschen Gerät ausgeführt hat, weiß wirklich, wie fragil textbasierte Selektoren sein können.