Das UI Automator-Testframework bietet eine Reihe von APIs zum Erstellen von UI-Tests, die mit Nutzer- und System-Apps interagieren.
Einführung in moderne UI Automator-Tests
Mit UI Automator 2.4 wird eine optimierte, Kotlin-freundliche Domain Specific Language (DSL) eingeführt, die das Schreiben von UI-Tests für Android vereinfacht. Diese neue API-Oberfläche konzentriert sich auf das Suchen von Elementen auf Grundlage von Prädikaten und die explizite Steuerung von App-Zuständen. Damit können Sie verwaltbarere und zuverlässigere automatisierte Tests erstellen.
Mit UI Automator können Sie eine App außerhalb des App-Prozesses testen. So können Sie Release-Versionen mit angewendeter Minimierung testen. UI Automator ist auch beim Schreiben von Makrobenchmark-Tests hilfreich.
Zu den wichtigsten Funktionen des modernen Ansatzes gehören:
- Ein dedizierter
uiAutomator
-Testbereich für übersichtlicheren und ausdrucksstärkeren Testcode. - Methoden wie
onElement
,onElements
undonElementOrNull
zum Suchen von UI-Elementen mit eindeutigen Attributen. - Integrierter Wartemechanismus für bedingte Elemente
onElement*(timeoutMs: Long = 10000)
- Explizite Verwaltung des App-Status, z. B.
waitForStable
undwaitForAppToBeVisible
. - Direkte Interaktion mit Knoten von Barrierefreiheitsfenstern für Multi-Window-Testszenarien.
- Integrierte Screenshot-Funktionen und eine
ResultsReporter
für visuelle Tests und Fehlerbehebung.
Projekt einrichten
Wenn Sie die modernen UI Automator-APIs verwenden möchten, müssen Sie die Datei build.gradle.kts
Ihres Projekts aktualisieren, um die neueste Abhängigkeit einzufügen:
Kotlin
dependencies {
...
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.4.0-alpha05")
}
Groovy
dependencies {
...
androidTestImplementation "androidx.test.uiautomator:uiautomator:2.4.0-alpha05"
}
Wichtige API-Konzepte
In den folgenden Abschnitten werden die wichtigsten Konzepte der modernen UI Automator API beschrieben.
uiAutomator-Testbereich
Greifen Sie auf alle neuen UI Automator-APIs innerhalb des uiAutomator { ... }
-Blocks zu. Mit dieser Funktion wird ein UiAutomatorTestScope
erstellt, das eine prägnante und typsichere Umgebung für Ihre Testvorgänge bietet.
uiAutomator {
// All your UI Automator actions go here
startApp("com.example.targetapp")
onElement { textAsString() == "Hello, World!" }.click()
}
UI-Elemente finden
Verwenden Sie UI Automator-APIs mit Prädikaten, um UI-Elemente zu finden. Mit diesen Prädikaten können Sie Bedingungen für Eigenschaften wie Text, ausgewählter oder fokussierter Status und Inhaltsbeschreibung definieren.
onElement { predicate }
: Gibt das erste UI-Element zurück, das innerhalb eines Standardzeitlimits mit dem Prädikat übereinstimmt. Die Funktion löst eine Ausnahme aus, wenn sie kein passendes Element findet.// 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 }
: Ähnlich wieonElement
, gibt abernull
zurück, wenn die Funktion innerhalb des Zeitlimits kein übereinstimmendes Element findet. Es wird keine Ausnahme ausgelöst. Verwenden Sie diese Methode für optionale Elemente.val optionalButton = onElementOrNull { textAsString() == "Skip" } optionalButton?.click() // Click only if the button exists
onElements { predicate }
: Wartet, bis mindestens ein UI-Element dem angegebenen Prädikat entspricht, und gibt dann eine Liste aller übereinstimmenden UI-Elemente zurück.// Get all items in a list Ui element val listItems = onElements { className == "android.widget.TextView" && isClickable } listItems.forEach { it.click() }
Hier sind einige Tipps zur Verwendung von onElement
-Aufrufen:
onElement
-Aufrufe für verschachtelte Elemente verketten: Sie könnenonElement
-Aufrufe verketten, um Elemente in anderen Elementen zu finden, wobei eine Über-/Unterordnungshierarchie berücksichtigt wird.// 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()
Geben Sie ein Zeitlimit für
onElement*
-Funktionen an, indem Sie einen Wert übergeben, der Millisekunden darstellt.// 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()
Mit UI-Elementen interagieren
Mit UI-Elementen interagieren, indem Sie Klicks simulieren oder Text in bearbeitbare Felder eingeben.
// 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()
App-Status und Watcher verarbeiten
Sie können den Lebenszyklus Ihrer App verwalten und unerwartete UI-Elemente behandeln, die während Ihrer Tests möglicherweise angezeigt werden.
Verwaltung des App-Lebenszyklus
Die APIs bieten Möglichkeiten, den Status der zu testenden App zu steuern:
// 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")
Unerwartete Benutzeroberfläche verarbeiten
Mit der watchFor
API können Sie Handler für unerwartete UI-Elemente wie Berechtigungsdialogfelder definieren, die während des Testablaufs angezeigt werden können. Dabei wird der interne Watcher-Mechanismus verwendet, der jedoch mehr Flexibilität bietet.
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
ist ein Beispiel für einen ScopedWatcher<T>
, wobei T
das Objekt ist, das als Bereich an den Block in watchFor
übergeben wird. Sie können benutzerdefinierte Watcher auf Grundlage dieses Musters erstellen.
Auf App-Sichtbarkeit und -Stabilität warten
Manchmal müssen Tests warten, bis Elemente sichtbar oder stabil werden. UI Automator bietet mehrere APIs, die Ihnen dabei helfen.
Mit waitForAppToBeVisible("com.example.targetapp")
wird bis zu einem anpassbaren Zeitlimit gewartet, bis ein UI-Element mit dem angegebenen Paketnamen auf dem Bildschirm angezeigt wird.
// Wait for the app to be visible after launching it
startApp("com.example.targetapp")
waitForAppToBeVisible("com.example.targetapp")
Verwenden Sie die waitForStable()
API, um zu prüfen, ob die Benutzeroberfläche der App als stabil gilt, bevor Sie mit ihr interagieren.
// 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()
Erweiterte Funktionen
Die folgenden Funktionen sind für komplexere Testszenarien nützlich.
Mit mehreren Fenstern interagieren
Mit den UI Automator-APIs können Sie direkt mit UI-Elementen interagieren und sie untersuchen. Das ist besonders nützlich für Szenarien mit mehreren Fenstern, z. B. im Bild-im-Bild-Modus (BiB) oder bei Splitscreen-Layouts.
// 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()
Screenshots und visuelle Behauptungen
Sie können Screenshots des gesamten Bildschirms, bestimmter Fenster oder einzelner UI-Elemente direkt in Ihren Tests erstellen. Das ist hilfreich für visuelle Regressionstests und das Debuggen.
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"))
}
Die Erweiterungsfunktion saveToFile
für Bitmap vereinfacht das Speichern des aufgenommenen Bildes in einem angegebenen Pfad.
ResultsReporter zur Fehlerbehebung verwenden
Mit dem ResultsReporter
können Sie Testartefakte wie Screenshots direkt mit Ihren Testergebnissen in Android Studio verknüpfen, um sie einfacher zu prüfen und zu debuggen.
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()
}
Von älteren UI Automator-Versionen migrieren
Wenn Sie bereits UI Automator-Tests mit älteren API-Oberflächen haben, können Sie die folgende Tabelle als Referenz für die Migration zum modernen Ansatz verwenden:
Aktionstyp | Alte UI Automator-Methode | Neue UI Automator-Methode |
---|---|---|
Einstiegspunkt | UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) |
Schließen Sie die Testlogik in den uiAutomator { ... } -Bereich ein. |
UI-Elemente finden | device.findObject(By.res("com.example.app:id/my_button")) |
onElement { id == "my\_button" } |
UI-Elemente finden | device.findObject(By.text("Click Me")) |
onElement { textAsString() == "Click Me" } |
Auf inaktive Benutzeroberfläche warten | device.waitForIdle() |
Bevorzuge den integrierten Zeitlimitmechanismus von onElement ; andernfalls activeWindow().waitForStable() |
Untergeordnete Elemente finden | Manuell verschachtelte findObject -Aufrufe |
onElement().onElement() Verkettung |
Berechtigungsdialogfelder verarbeiten | UiAutomator.registerWatcher() |
watchFor(PermissionDialog) |