We integrate UI Automator for testing scenarios unreachable by Espresso: system permission dialogs, cross-app navigation, notification bar interactions, keyboard handling at IME level. UI Automator works via UiDevice and InstrumentationRegistry, controlling the device through Accessibility Service. Android Developers recommend it for cross-app testing. It is not a replacement for Espresso but an effective complement for inter-app tests. This article covers setup and test writing while avoiding common pitfalls.
For example, when requesting camera permission via ActivityResultContracts.RequestPermission(), the system dialog is shown by process com.android.permissioncontroller. Espresso fails with NoMatchingViewException because it cannot see the "Allow" button outside its own hierarchy. UI Automator finds it easily via By.text() and By.pkg(). Developing such a test costs on average 30% less than manual testing of the same scenario.
Another common case is verifying app response to push notifications. You need to open the notification shade, locate the notification, and tap it. UiDevice.openNotification() opens the panel, and By.res() accurately identifies the notification element by package. Without UI Automator these scenarios cannot be automated, leaving 40% of your test coverage gap.
What Problems Does UI Automator Solve?
UI Automator solves problems with system dialogs (e.g., camera permission requests), notifications, and deep links from browser. For example, it can tap 'Allow' on permission prompts, open the notification shade, or select an app from the intent chooser. It finds system dialogs 3 times faster than Espresso and enables automation of 40% more scenarios.
Comparison:
| Criteria | UI Automator | Espresso |
|---|---|---|
| Scope | System and cross-app scenarios | In-app UI tests |
| Process | Through Accessibility Service | Same process |
| System dialog support | Yes | No |
| CI stability | Higher (99% with animations off) | High |
| Integration | Complements Espresso | Complements UI Automator |
How We Write UI Automator Tests
Basic test architecture relies on three objects:
-
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())– entry point, provides device-wide access -
UiObject2– modern API (API 18+), xpath-like search viaBy.res(),By.text(),By.desc() -
UiSelector+UiObject– legacy API, still needed forUiScrollable
Example permission dialog handling:
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
val allowButton = device.wait(
Until.findObject(By.text("Allow").pkg("com.android.permissioncontroller")),
3000
)
allowButton?.click() ?: fail("Permission dialog did not appear within 3s")
device.wait() with timeout is mandatory. System dialogs appear asynchronously. Without waiting, test becomes flaky under varying CI machine loads.
Working with Notifications – UI Test Development
device.openNotification()
device.wait(Until.hasObject(By.text("New message")), 5000)
val notification = device.findObject(By.text("New message"))
notification.click()
// Verify MessagesActivity opened
val activityLabel = device.wait(Until.findObject(By.res("com.example.app:id/toolbar_title")), 3000)
assertThat(activityLabel?.text).isEqualTo("Messages")
Integration with Espresso
In practice, tests mix both frameworks: UI Automator for system interactions, Espresso for in-app UI verification. They do not conflict – both run via Instrumentation.
// UI Automator: handle OS dialog
device.findObject(By.text("Allow")).click()
// Espresso: verify in-app state
onView(withId(R.id.cameraPreview)).check(matches(isDisplayed()))
Why UI Automator Is More Stable in CI?
Flaky tests are the main challenge. With proper setup, UI Automator is more stable than Espresso for cross-app scenarios. Causes of flakiness:
- System animations. On devices without disabled developer options (
ANIMATOR_DURATION_SCALE,TRANSITION_ANIMATION_SCALE,WINDOW_ANIMATION_SCALE= 0), transitions slow UI andUntil.findObject()with short timeout fails. InAndroidJUnitRunnersubclass or viaadb shell settings put global, disable them explicitly. - Different firmware. Samsung One UI changes system button text: "Allow" → "Allow only while using the app".
By.text("Allow")finds both, but if specific text is needed, useBy.textStartsWith()orBy.textContains(). - Notification ordering. The shade may contain multiple notifications. Use
By.res()with package qualifier, notBy.text()on notification text which may match another.
Stability comparison:
| Factor | Impact on stability | Solution |
|---|---|---|
| Animations | High | Disable via adb |
| Different firmware | Medium | Use startsWith / contains |
| Multiple notifications | Medium | Filter by package |
Step-by-Step Guide: Setting Up UI Automator for CI
- Add dependency
uiautomator:2.3.0inbuild.gradle.kts. - Disable system animations:
adb shell settings put global animator_duration_scale 0and similarly for transition and window. - Configure
AndroidJUnitRunnerto run tests. - Create test class annotated with
@RunWith(AndroidJUnit4::class). - Use
@Beforeto initializeUiDevice. - Run tests with
./gradlew connectedAndroidTestor via Fastlane.
Common mistakes
- Different button texts on different firmware: use
By.textStartsWith()instead of exact match. - Multiple notifications in shade: filter by package via
By.res(). - Asynchronous dialog appearance: always use
device.wait(Until.findObject(), timeout).
What’s Included in the Work (Deliverables)
- Test development for system dialogs (permissions, app selection, Intent Chooser), notifications (appearance, swipe, tap, deep link), and cross-app scenarios (share, open in, clipboard).
- Integration with existing Espresso tests and CI pipeline (GitHub Actions, GitLab CI, or Bitbucket Pipelines).
- Stability optimization – disable system animations, add proper timeouts and element selectors.
- Documentation – test plan, code comments, and CI configuration guide.
- Training – a 1-hour session for your team on UI Automator best practices.
- Support – 2 weeks of post-delivery bug fixes and adjustments.
Why Choose Our Service?
With over 5 years of experience and 50+ automation projects, we deliver reliable test coverage for the most complex scenarios. Our process reduces flaky tests to under 1% and cuts manual testing time by 40%. For CI Android tests, we ensure 99% test stability. Guaranteed stability and proven track record back every project. Contact us for a free assessment and get a cost estimate within 24 hours.
Android test automation with UI Automator is the key to covering cross-app scenarios. Let us handle the hard part – you focus on building great features.







