UI Automator — это фреймворк от Google для автоматизированного UI-тестирования Android-приложений, который работает на уровне системы и может взаимодействовать с элементами интерфейса за пределами одного приложения. В отличие от Espresso, UI Automator не привязан к процессу конкретного приложения: он способен открывать системные диалоги, Notification-шторку и переключаться между приложениями. По данным Google Android Developers, UI Automator использует стандартный Accessibility Service для доступа к UI-дереву устройства.
Главное
UI Automator — это фреймворк для функционального UI-тестирования Android, который работает на уровне операционной системы. Он предоставляет API для доступа к любому элементу на экране устройства, независимо от того, какому приложению он принадлежит — включая системную строку состояния, диалоги разрешений, домашний экран и сторонние приложения. Это делает его незаменимым для тестирования сценариев, выходящих за пределы одного приложения.
Архитектурно UI Automator использует Accessibility Service — тот же сервис, который используется TalkBack, Switch Access и другими инструментами доступности. Через этот сервис фреймворк получает полное дерево UI-компонентов текущего экрана и позволяет выполнять над ними действия: нажатие, свайп, ввод текста, долгое нажатие.
UI Automator впервые появился в Android 4.3 (API 18) и с тех пор является частью Android Testing Support Library как официальный инструмент Google для кросс-приложенческого тестирования. В AndroidX Test он доступен как отдельный артефакт androidx.test.uiautomator:uiautomator версии 2.3.0 (2024), который поддерживает все версии Android от API 18.
Принцип работы UI Automator основан на сканировании Accessibility-дерева текущего экрана. При вызове метода findObject(selector) фреймворк обходит иерархию View, находит первый элемент, соответствующий условиям UiSelector, и возвращает объект UiObject — прокси для взаимодействия с реальным View.
Типичный тест UI Automator начинается с получения экземпляра UiDevice, который представляет физическое устройство. UiDevice предоставляет методы для поиска элементов, управления нажатиями кнопок (Home, Back, Recent), поворота экрана и скриншотов. После поиска элемента через UiSelector выполняются действия над UiObject.
В примере ниже тест открывает приложение Settings, находит пункт «Батарея» через текст и нажимает его. UI Automator не требуется запускать Activity — он работает с любым экраном устройства, включая сторонние приложения.
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
// Открыть экран настроек
device.pressHome()
device.wait(Until.hasObject(UiSelector().text("Настройки")), 2000)
// Найти пункт "Батарея" и нажать
val batteryItem = device.findObject(
UiSelector().text("Батарея")
)
batteryItem.clickAndWait(Until.newWindow(), 3000)
UiDevice — главный класс для взаимодействия с устройством. Он предоставляет методы для поиска элементов, симуляции нажатий аппаратных кнопок (Home, Back, Menu, Volume), управления питанием, скриншотов и ожидания определённых состояний экрана. UiDevice создаётся один раз на тест и переиспользуется для всех операций.
UiSelector — это fluent API для поиска UI-элементов. В отличие от Espresso ViewMatchers, UiSelector не требует компиляции — условия поиска формируются через цепочку методов: text(), className(), description(), resourceId(), index(). Несколько условий комбинируются автоматически через логическое AND.
| Метод UiSelector | Назначение |
|---|---|
| text(String) | Поиск по точному тексту элемента |
| textContains(String) | Поиск по части текста |
| resourceId(String) | Поиск по ID ресурса (например, com.example:id/button) |
| className(String) | Поиск по имени класса View |
| description(String) | Поиск по content-description |
| childSelector(selector) | Поиск дочернего элемента в контейнере |
Когда на экране несколько элементов с одинаковым текстом, UiSelector позволяет комбинировать критерии: найти контейнер по ID, затем внутри него — элемент по тексту и классу. Это гарантирует уникальную идентификацию нужного компонента. Метод childSelector сужает область поиска до указанного контейнера, что ускоряет навигацию по UI-дереву.
val scrollView = device.findObject(
UiSelector().resourceId("android:id/list")
)
// Внутри списка найти элемент с текстом "Wi-Fi"
val wifiItem = scrollView.findObject(
UiSelector().text("Wi-Fi")
wifiItem.click()
Cross-application (межприложенческое) тестирование — главная функция, ради которой выбирают UI Automator. Фреймворк может переключаться между приложениями, тестировать OAuth-логин через браузер, проверять системные диалоги (разрешения, выбор приложения) и взаимодействовать с системной строкой состояния, панелью уведомлений и экраном блокировки.
Типичный сценарий cross-app теста: приложение открывает браузер для OAuth-авторизации, пользователь вводит логин и пароль, браузер перенаправляет обратно в приложение. UI Automator переключается между процессами, находит поля ввода в браузере, заполняет их и нажимает «Войти».
// Ожидание появления браузера
device.wait(Until.hasObject(
UiSelector().packageName("com.android.chrome")
), 5000)
// Поиск поля ввода email в браузере
val emailField = device.findObject(
UiSelector().className("android.widget.EditText").instance(0)
)
emailField.text = "user@example.com"
UI Automator может проверять и закрывать системные диалоги — разрешения на геолокацию, уведомления, доступ к файлам. Это критически важно для тестирования первого запуска приложения, когда система последовательно запрашивает несколько разрешений. Без UI Automator такие сценарии невозможно автоматизировать, так как системные диалоги не принадлежат процессу приложения.
Выбор между UI Automator и Espresso зависит от сценария тестирования. Espresso оптимизирован для тестирования одного приложения с автоматической синхронизацией и минимальным boilerplate. UI Automator подходит для сценариев, где нужно взаимодействовать с системой, браузером или несколькими приложениями.
| Критерий | UI Automator | Espresso |
|---|---|---|
| Область | Всё устройство, несколько приложений | Одно приложение |
| Синхронизация | Ручная (wait, sleep) | Автоматическая (Idling Resource) |
| Скорость | Медленнее (доступ через сервис) | Быстрее (работает внутри процесса) |
| System UI | Поддерживает (Notifications, Quick Settings) | Не поддерживает |
| Точность поиска | UiSelector по атрибутам | ViewMatchers по типу и иерархии |
| Стабильность | Ниже (зависит от таймингов) | Выше (автоматическое ожидание) |
На практике эти фреймворки часто используются вместе: Espresso покрывает UI-тесты основного приложения с высокой стабильностью, а UI Automator подключается для сценариев, выходящих за границы приложения — OAuth-логин, системные разрешения, работа с Share Intent. Такая комбинация даёт максимальное покрытие UI при минимальных затратах на поддержку тестов.
Подключение UI Automator выполняется через добавление зависимости в build.gradle. Фреймворк входит в AndroidX Test и не требует дополнительных разрешений в манифесте — доступ к Accessibility Service настраивается автоматически при запуске инструментального теста.
Минимальная конфигурация включает артефакт uiautomator и стандартный тестовый раннер AndroidJUnitRunner. Тесты UI Automator размещаются в директории src/androidTest и запускаются на эмуляторе или физическом устройстве с Android API 18+.
dependencies {
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.3.0")
androidTestImplementation("androidx.test.ext:junit:1.2.1")
androidTestImplementation("androidx.test:runner:1.6.1")
}
Для получения экземпляра UiDevice используется InstrumentationRegistry.getInstrumentation(). UiDevice следует создавать один раз в методе setUp() и переиспользовать во всех тестах класса для экономии ресурсов устройства. Важно учитывать, что UiDevice не потокобезопасен — все операции должны выполняться в одном потоке тестового метода. Создание нового UiDevice в каждом тесте приводит к накладным расходам и замедлению выполнения. Рекомендуется создавать UiDevice один раз в методе beforeClass и переиспользовать для всех тестов тестового класса.
В отличие от Espresso, UI Automator не имеет автоматической синхронизации. Для ожидания появления элементов используется метод UiDevice.wait(condition, timeout) с объектом Until: Until.findObject(selector), Until.hasObject(selector), Until.gone(selector). Без корректных ожиданий тесты становятся flaky из-за race condition — элемент может не успеть появиться на экране к моменту поиска. Рекомендуется устанавливать таймаут не менее 3-5 секунд для стабильности.
Часто задаваемые вопросы
UI Automator работает на уровне Accessibility Service и может взаимодействовать с любыми приложениями. Espresso работает внутри процесса одного приложения и использует автоматическую синхронизацию с UI-потоком. UI Automator лучше для межприложенческих сценариев, Espresso — для стабильных тестов одного приложения.
Да, UI Automator работает на всех устройствах с Android API 18+. Он не требует root-доступа — используется стандартный Accessibility Service, который активируется через Instrumentation при запуске тестов.
UI Automator использует Accessibility Service для получения полного дерева UI-компонентов текущего экрана. Затем UiSelector обходит это дерево и находит элементы по заданным критериям: текст, класс, ID, content-description или их комбинация.
Да, метод UiDevice.takeScreenshot(storePath) позволяет сделать скриншот текущего экрана и сохранить его в файл. Это полезно для отладки: при падении теста можно сохранить скриншот и проанализировать состояние экрана.
UI Automator не имеет автоматической синхронизации, поэтому тесты чувствительны к таймингам. Если анимация не завершилась или View не успел отрисоваться, findObject может не найти элемент. Решение — использовать UiDevice.wait() с достаточным таймаутом.
Итоги
Набор инструментов UI Automator охватывает все ключевые сценарии межприложенческого тестирования и является стандартом для Android-автоматизации на уровне системы.
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также