Фреймворк тестирования 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 { viewIdResourceName == "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 existsonElements { 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 { viewIdResourceName == "first" } .onElement { viewIdResourceName == "second" } .onElement { viewIdResourceName == "third" } .click()Укажите тайм-аут для функций
onElement*, передав значение в миллисекундах.// Find a Ui element with a zero timeout (instant check) onElement(0) { viewIdResourceName == "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 { viewIdResourceName == "my_loading_indicator" }.waitForStable()
Используйте UI Automator для макротестов и базовых профилей.
Используйте UI Automator для тестирования производительности с помощью Jetpack Macrobenchmark и для создания базовых профилей , поскольку он предоставляет надежный способ взаимодействия с вашим приложением и измерения производительности с точки зрения конечного пользователя.
Macrobenchmark использует API UI Automator для управления пользовательским интерфейсом и измерения взаимодействий. Например, в тестах на скорость запуска можно использовать onElement для определения момента полной загрузки контента пользовательского интерфейса, что позволяет измерить время до полного отображения (TTFD) . В тестах на скорость загрузки приложения API UI Automator используются для прокрутки списков или запуска анимаций для измерения времени отклика кадров. Такие функции, как startActivity() или startIntent() полезны для перевода приложения в правильное состояние перед началом измерения.
При создании базовых профилей вы автоматизируете критически важные пользовательские сценарии (CUJ) вашего приложения, чтобы зафиксировать, какие классы и методы требуют предварительной компиляции. UI Automator — идеальный инструмент для написания таких скриптов автоматизации. Поиск элементов на основе предикатов и встроенные механизмы ожидания ( onElement ) в современном DSL обеспечивают более надежное и детерминированное выполнение тестов по сравнению с другими методами. Эта стабильность снижает нестабильность и гарантирует, что созданный базовый профиль точно отражает пути выполнения кода во время наиболее важных пользовательских сценариев.
Расширенные функции
Следующие функции полезны для более сложных сценариев тестирования.
Взаимодействие с несколькими окнами
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 { viewIdResourceName == "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 { viewIdResourceName == "my\_button" } |
| Найти элементы пользовательского интерфейса | device.findObject(By.text("Click Me")) | onElement { textAsString() == "Click Me" } |
| Дождитесь завершения работы интерфейса в режиме ожидания. | device.waitForIdle() | Предпочтительнее использовать встроенный механизм таймаута метода onElement ; в противном случае используйте activeWindow().waitForStable() |
| Найти дочерние элементы | Вложенные вызовы findObject , выполняемые вручную | цепочка вызовов onElement().onElement() |
| Обработка диалоговых окон с запросами разрешений | UiAutomator.registerWatcher() | watchFor(PermissionDialog) |