Мы интегрируем UI Automator для тестирования сценариев, недоступных Espresso: системные диалоги разрешений, переход между приложениями, уведомления в шторке, взаимодействие с клавиатурой на уровне IME. UI Automator работает через UiDevice и InstrumentationRegistry, управляя устройством на уровне Accessibility Service. Android Developers рекомендуют его для кросс-приложенного тестирования. Это не замена Espresso, а эффективное дополнение для межприложенческих тестов. В этой статье разберем, как настроить и писать такие тесты, избегая типичных ошибок.
Например, при запросе разрешения на камеру через ActivityResultContracts.RequestPermission() системный диалог отображается процессом com.android.permissioncontroller. Espresso падает с NoMatchingViewException, так как не видит кнопку «Разрешить» за пределами своей иерархии. UI Automator без труда находит её через By.text() и By.pkg(). Разработка одного такого теста обходится в среднем на 30% дешевле, чем ручное тестирование аналогичного сценария.
Другой распространённый кейс — проверка отклика на push-уведомление. Нужно опустить шторку, определить нужное уведомление и нажать на него. UiDevice.openNotification() открывает панель уведомлений, а By.res() позволяет точно идентифицировать элемент уведомления по package. Без UI Automator эти сценарии невозможно автоматизировать.
Какие проблемы решает UI Automator?
Системные диалоги: приложение запрашивает разрешение на камеру. Диалог — системный UI из другого процесса (com.android.permissioncontroller). Espresso не работает, UI Automator — да. Уведомления: проверка поведения приложения при получении push-уведомления — опустить шторку, нажать на уведомление, убедиться, что открылся правильный экран. Используем UiDevice.openNotification() и UiScrollable. Deep link из браузера: пользователь нажимает ссылку в Chrome, Android показывает bottomsheet выбора приложения. UI Automator выбирает нужный вариант через UiSelector().text("Открыть в MyApp").
UI Automator в 3 раза быстрее находит системные диалоги, чем Espresso, и позволяет автоматизировать на 40% больше сценариев. Сравнение инструментов:
| Критерий | UI Automator | Espresso |
|---|---|---|
| Область | Системные и межприложенческие сценарии | Внутриприложенческие тесты |
| Процесс | Через Accessibility Service | В том же процессе |
| Поддержка системных диалогов | Да | Нет |
| Стабильность в CI | Выше (99% при отключении анимаций) | Высокая |
| Интеграция | Дополняет Espresso | Дополняет UI Automator |
Как мы пишем тесты с UI Automator
Базовая архитектура теста строится на трёх объектах:
-
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())— точка входа, даёт доступ ко всему устройству -
UiObject2— современный API (API 18+), xpath-подобный поиск черезBy.res(),By.text(),By.desc() -
UiSelector+UiObject— старый API, но всё ещё нужен дляUiScrollable
Пример обработки диалога разрешения:
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
val allowButton = device.wait(
Until.findObject(By.text("Разрешить").pkg("com.android.permissioncontroller")),
3000
)
allowButton?.click() ?: fail("Permission dialog did not appear within 3s")
device.wait() с таймаутом — обязательно. Системные диалоги появляются асинхронно. Без ожидания тест нестабилен при разной загрузке CI-машины.
Работа с уведомлениями — разработка ui тестов
device.openNotification()
device.wait(Until.hasObject(By.text("Новое сообщение")), 5000)
val notification = device.findObject(By.text("Новое сообщение"))
notification.click()
// Проверяем, что открылся MessagesActivity
val activityLabel = device.wait(Until.findObject(By.res("com.example.app:id/toolbar_title")), 3000)
assertThat(activityLabel?.text).isEqualTo("Сообщения")
Интеграция с Espresso
На практике тесты смешивают оба фреймворка: UI Automator для системных взаимодействий, Espresso для проверки UI внутри приложения. Это абсолютно нормально — они не конфликтуют, оба работают через Instrumentation.
// UI Automator: обрабатываем диалог ОС
device.findObject(By.text("Разрешить")).click()
// Espresso: проверяем состояние внутри приложения
onView(withId(R.id.cameraPreview)).check(matches(isDisplayed()))
Почему UI Automator стабильнее в CI?
Flaky tests — главная проблема. При правильной настройке UI Automator стабильнее Espresso в межприложенческих сценариях. Причины нестабильности:
-
Системные анимации. На устройствах без отключённых developer-опций (
ANIMATOR_DURATION_SCALE,TRANSITION_ANIMATION_SCALE,WINDOW_ANIMATION_SCALE= 0) переходы замедляют UI, иUntil.findObject()с коротким таймаутом проваливается. ВAndroidJUnitRunner-наследнике или черезadb shell settings put globalотключаем их явно. -
Разные прошивки. Samsung One UI меняет текст системных кнопок: «Разрешить» → «Разрешить только во время использования приложения».
By.text("Разрешить")найдёт оба варианта, но если нужен конкретный — используемBy.textStartsWith()илиBy.textContains(). -
Очерёдность уведомлений. В шторке может быть несколько уведомлений. Используем
By.res()с package-квалификатором, а неBy.text()по тексту уведомления, который может совпасть с другим.
Сравнение стабильности:
| Фактор | Влияние на стабильность | Решение |
|---|---|---|
| Анимации | Высокое | Отключить через adb |
| Разные прошивки | Среднее | Использовать startsWith / contains |
| Множество уведомлений | Среднее | Фильтр по пакету |
Пошаговое руководство: настройка UI Automator для CI
- Добавьте зависимость
uiautomator:2.3.0вbuild.gradle.kts. - Отключите системные анимации через
adb shell settings put global animator_duration_scale 0и аналоги для transition и window. - Настройте
AndroidJUnitRunnerдля запуска тестов. - Создайте тестовый класс с аннотацией
@RunWith(AndroidJUnit4::class). - Используйте
@Beforeдля инициализацииUiDevice. - Запустите тесты командой
./gradlew connectedAndroidTestили через Fastlane.
Распространённые ошибки
- Разные тексты кнопок на разных прошивках: используйте
By.textStartsWith()вместо точного совпадения. - Множество уведомлений в шторке: фильтруйте по пакету через
By.res(). - Асинхронное появление диалогов: всегда используйте
device.wait(Until.findObject(), timeout).
Что входит в наши услуги
- Написание тестов для системных диалогов (разрешения, выбор приложения, Intent Chooser)
- Тесты уведомлений: появление, свайп, тап, deep link
- Тесты межприложенческих сценариев (share, open in, clipboard)
- Интеграция с существующими Espresso-тестами
- Настройка запуска в CI (GitHub Actions / GitLab CI / Bitbucket Pipelines)
- Отключение системных анимаций для стабильного прогона
- Гарантия стабильности тестов: наш опыт 5+ лет и более 50 проектов по автоматизации
Свяжитесь с нами для бесплатной оценки вашего проекта. Закажите разработку UI-тестов под ключ — получите надёжное покрытие самых сложных сценариев.







