Фреймворк тестирования UI Automator предоставляет набор API для создания тестов пользовательского интерфейса, взаимодействующих с пользовательскими и системными приложениями.
Введение в современное тестирование UI Automator
UI Automator 2.4 представляет собой оптимизированный, совместимый с Kotlin предметно-ориентированный язык (DSL), который упрощает написание UI-тестов для Android. Этот новый API-интерфейс ориентирован на поиск элементов на основе предикатов и явный контроль над состояниями приложения. Используйте его для создания более простых в обслуживании и надежных автоматизированных тестов.
UI Automator позволяет тестировать приложение вне процесса его разработки. Это позволяет тестировать версии релиза с применением минификации. UI Automator также помогает при написании макробенчмарк-тестов.
Ключевые особенности современного подхода включают в себя:
- Специальная тестовая область
uiAutomator
для более чистого и выразительного тестового кода. - Такие методы, как
onElement
,onElements
иonElementOrNull
для поиска элементов пользовательского интерфейса с четкими предикатами. - Встроенный механизм ожидания для условных элементов
onElement*(timeoutMs: Long = 10000)
- Явное управление состоянием приложения, такое как
waitForStable
иwaitForAppToBeVisible
. - Прямое взаимодействие с узлами окна доступности для многооконного тестирования.
- Встроенные возможности создания снимков экрана и
ResultsReporter
для визуального тестирования и отладки.
Настройте свой проект
Чтобы начать использовать современные API UI Automator, обновите файл build.gradle.kts
вашего проекта, включив в него последнюю зависимость :
Котлин
dependencies {
...
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.4.0-alpha05")
}
Круто
dependencies {
...
androidTestImplementation "androidx.test.uiautomator:uiautomator:2.4.0-alpha05"
}
Основные концепции API
В следующих разделах описываются основные концепции современного API UI Automator.
Область тестирования uiAutomator
Доступ ко всем новым API UI Automator осуществляется через блок uiAutomator { ... }
. Эта функция создаёт UiAutomatorTestScope
, предоставляющий лаконичную и типобезопасную среду для тестовых операций.
uiAutomator {
// All your UI Automator actions go here
startApp("com.example.targetapp")
onElement { textAsString() == "Hello, World!" }.click()
}
Найти элементы пользовательского интерфейса
Используйте API UI Automator с предикатами для поиска элементов пользовательского интерфейса. Эти предикаты позволяют определять условия для таких свойств, как текст, выбранное или находящееся в фокусе состояние, а также описание содержимого.
onElement { predicate }
: возвращает первый элемент пользовательского интерфейса, соответствующий предикату, в течение заданного по умолчанию времени ожидания. Функция генерирует исключение, если соответствующий элемент не найден.// 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 }
: АналогичноonElement
, но возвращаетnull
если функция не находит соответствующий элемент в течение заданного времени ожидания. Исключение не генерируется. Используйте этот метод для необязательных элементов.val optionalButton = onElementOrNull { textAsString() == "Skip" } optionalButton?.click() // Click only if the button exists
onElements { predicate }
: ждет, пока хотя бы один элемент пользовательского интерфейса не будет соответствовать заданному предикату, затем возвращает список всех соответствующих элементов пользовательского интерфейса.// Get all items in a list Ui element val listItems = onElements { className == "android.widget.TextView" && isClickable } listItems.forEach { it.click() }
Вот несколько советов по использованию вызовов onElement
:
Цепочка вызовов
onElement
для вложенных элементов: вы можете сцеплять вызовыonElement
для поиска элементов внутри других элементов, следуя иерархии родитель-потомок.// 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()
Укажите время ожидания для функций
onElement*
, передав значение, представляющее миллисекунды.// 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()
Взаимодействие с элементами пользовательского интерфейса
Взаимодействуйте с элементами пользовательского интерфейса, имитируя щелчки или вводя текст в редактируемые поля.
// 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()
Управление состояниями приложений и наблюдателями
Управляйте жизненным циклом вашего приложения и обрабатывайте непредвиденные элементы пользовательского интерфейса, которые могут появиться во время тестов.
Управление жизненным циклом приложений
API предоставляют способы управления состоянием тестируемого приложения:
// 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")
Обработка непредвиденных ошибок пользовательского интерфейса
API watchFor
позволяет определять обработчики непредвиденных элементов пользовательского интерфейса, таких как диалоговые окна с запросами разрешений, которые могут появляться во время тестирования. Этот подход использует внутренний механизм наблюдателей, но обеспечивает большую гибкость.
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
— пример ScopedWatcher<T>
, где T
— объект, переданный в качестве области действия блоку в watchFor
. Вы можете создавать собственные наблюдатели на основе этого шаблона.
Дождитесь видимости и стабильности приложения
Иногда тестам необходимо дождаться, пока элементы станут видимыми или стабильными. UI Automator предлагает несколько API для решения этой проблемы.
Функция waitForAppToBeVisible("com.example.targetapp")
ожидает появления на экране элемента пользовательского интерфейса с заданным именем пакета в течение настраиваемого времени ожидания.
// Wait for the app to be visible after launching it
startApp("com.example.targetapp")
waitForAppToBeVisible("com.example.targetapp")
Используйте API waitForStable()
чтобы убедиться, что пользовательский интерфейс приложения считается стабильным, прежде чем взаимодействовать с ним.
// 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()
Расширенные функции
Следующие функции полезны для более сложных сценариев тестирования.
Взаимодействие с несколькими окнами
API UI Automator позволяют напрямую взаимодействовать с элементами пользовательского интерфейса и просматривать их. Это особенно полезно в сценариях с несколькими окнами, например, в режиме «картинка в картинке» (PiP) или в режимах с разделённым экраном.
// 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()
Скриншоты и визуальные утверждения
Делайте снимки экрана всего экрана, отдельных окон или отдельных элементов пользовательского интерфейса непосредственно в тестах. Это полезно для визуального регрессионного тестирования и отладки.
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"))
}
Функция расширения saveToFile
для Bitmap упрощает сохранение захваченного изображения по указанному пути.
Используйте ResultsReporter для отладки
ResultsReporter
помогает вам связывать тестовые артефакты, такие как снимки экрана, напрямую с результатами тестирования в Android Studio для упрощения проверки и отладки.
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()
}
Миграция со старых версий UI Automator
Если у вас есть тесты UI Automator, написанные с использованием старых API-интерфейсов, используйте следующую таблицу в качестве справочного материала для перехода на современный подход:
Тип действия | Старый метод UI Automator | Новый метод UI Automator |
---|---|---|
Точка входа | UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) | Оберните логику теста в область uiAutomator { ... } . |
Найти элементы пользовательского интерфейса | device.findObject(By.res("com.example.app:id/my_button")) | onElement { id == "my\_button" } |
Найти элементы пользовательского интерфейса | device.findObject(By.text("Click Me")) | onElement { textAsString() == "Click Me" } |
Ожидание бездействия пользовательского интерфейса | device.waitForIdle() | Предпочитать встроенный механизм тайм-аута onElement ; в противном случае activeWindow().waitForStable() |
Найти дочерние элементы | Вложенные вручную вызовы findObject | цепочка onElement().onElement() |
Обработка диалоговых окон с разрешениями | UiAutomator.registerWatcher() | watchFor(PermissionDialog) |