Android UI Testing with Espresso: Development, Stability, CI

Developing UI Tests for Android Apps (Espresso) You wrote a login screen in Compose, set up Hilt, added Retrofit. You run the test — it fails. Why? Android UI tests are often flaky — the root cause is almost always ignoring IdlingResource. Developers insert `Thread.sleep(2000)` and hope it passes

Development and support of all types of mobile applications:

Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1All 1734 services
Android UI Testing with Espresso: Development, Stability, CI
Medium
~3-5 days

Our competencies:

Frequently Asked Questions

Latest works

  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    895
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    597

Developing UI Tests for Android Apps (Espresso)

You wrote a login screen in Compose, set up Hilt, added Retrofit. You run the test — it fails. Why? Android UI tests are often flaky — the root cause is almost always ignoring IdlingResource. Developers insert Thread.sleep(2000) and hope it passes — that's a path to chaos. Without properly configured IdlingResource, stability rarely exceeds 60%. A test suite of 100+ tests requires 2–3 hours to run, half fail due to timing.

We offer turnkey Espresso UI test development. We design architecture with the Robot pattern, configure IdlingResource for all asynchronous operations, and integrate with Firebase Test Lab. Within 3–5 days you get a stable set of critical user flows (login, payment, core UX). One of our projects — an internet banking app with 1,200 tests — achieved 98.5% stability after IdlingResource tuning.

Google officially recommends using IdlingResource to synchronize tests with background tasks — it's the foundation of stable automated testing.

Fundamentals and Critical Nuances

The basic Espresso test structure: onView(matcher).perform(action).check(assertion).

@Test fun loginWithValidCredentials_navigatesToHome() { onView(withId(R.id.emailInput)) .perform(typeText("[email protected]"), closeSoftKeyboard()) onView(withId(R.id.passwordInput)) .perform(typeText("password123"), closeSoftKeyboard()) onView(withId(R.id.loginButton)) .perform(click()) onView(withId(R.id.homeTitle)) .check(matches(isDisplayed())) } 

The most common cause of flaky tests is IdlingResource. If the app performs an asynchronous request (Retrofit, Coroutine), Espresso is unaware and tries to find the next element before the operation completes. The solution — register an IdlingResource.

// For OkHttp/Retrofit use OkHttpIdlingResource val idlingResource = OkHttpIdlingResource.create("okhttp", okHttpClient) IdlingRegistry.getInstance().register(idlingResource) // For coroutines use EspressoIdlingResource IdlingRegistry.getInstance().register(EspressoIdlingResource.getIdlingResource()) 

Espresso runs 2–3 times faster than UIAutomator because it avoids sleep.

Why Do Espresso Tests Fail?

Besides IdlingResource, a common issue is race conditions when working with RecyclerView or multithreading. Use IdlingResource for asynchronous operations and CountingIdlingResource for background tasks. In our experience, after implementing IdlingResource, test stability jumps from 60% to 95%.

Problem Solution
Asynchronous requests (Retrofit, Coroutine) OkHttpIdlingResource / EspressoIdlingResource
Image loading (Glide, Coil) GlideIdlingResource
Animations (Transition, Lottie) Disable animations in tests
RecyclerView with pagination CountingIdlingResource

Jetpack Compose UI Testing

If the project uses Compose, the Espresso approach is replaced by ComposeTestRule:

@get:Rule val composeTestRule = createAndroidComposeRule<MainActivity>() @Test fun loginScreen_showsErrorOnInvalidEmail() { composeTestRule.onNodeWithTag("emailInput") .performTextInput("invalid-email") composeTestRule.onNodeWithTag("loginButton") .performClick() composeTestRule.onNodeWithText("Invalid email format") .assertIsDisplayed() } 

testTag in Compose — the equivalent of accessibilityIdentifier in iOS. We always assign tags to all testable elements using Modifier.testTag("loginButton").

How to Speed Up Test Writing?

We use the Robot pattern — the Compose equivalent of Page Object. It makes tests read like scenarios and simplifies refactoring. Comparison:

Criterion Robot pattern Page Object
Syntax Call chains (Builder) Methods without returning object
Readability High, resembles test case steps Medium, requires additional structure
Reusability Through robot composition Through page class inheritance
Compose support Native, no extra wrappers Requires adaptation

Robot pattern is a better fit for Compose projects. Example:

class LoginRobot(private val composeTestRule: AndroidComposeTestRule<*, *>) { fun enterEmail(email: String) = apply { composeTestRule.onNodeWithTag("emailInput").performTextInput(email) } fun enterPassword(password: String) = apply { composeTestRule.onNodeWithTag("passwordInput").performTextInput(password) } fun clickLogin() = apply { composeTestRule.onNodeWithTag("loginButton").performClick() } fun assertHomeVisible() { composeTestRule.onNodeWithTag("homeScreen").assertIsDisplayed() } } // Test reads like a scenario @Test fun validLogin_showsHome() { LoginRobot(composeTestRule) .enterEmail("[email protected]") .enterPassword("pass123") .clickLogin() .assertHomeVisible() } 

Hilt and DI in Instrumented Tests

If the project uses Hilt, tests require HiltAndroidRule. The @BindValue annotation allows replacing a real repository with a fake without modifying production code — the test doesn't depend on the network or database.

How to Write a Stable Espresso Test in 5 Steps

  1. Define the scenario and identify asynchronous operations.
  2. Register IdlingResource for each operation.
  3. Use Robot pattern for structuring.
  4. Run the test locally, verify stability.
  5. Integrate with CI and Firebase Test Lab.

CI: Firebase Test Lab

A local emulator is sufficient for development, but before release — Firebase Test Lab with real devices. Example workflow (GitHub Actions):

- name: Run Espresso tests on Firebase Test Lab run: | gcloud firebase test android run \ --type instrumentation \ --app app/build/outputs/apk/debug/app-debug.apk \ --test app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk \ --device model=Pixel8,version=34 \ --device model=GalaxyS23,version=33 

Results — Logcat, screenshots on failure, run video — are stored in Google Cloud Storage. CI setup is included in the scope of work.

What's Included in Espresso UI Test Development

  • Designing test architecture (pattern selection, IdlingResource configuration)
  • Writing critical user flows (login, payment, core UX) with Robot pattern
  • CI/CD integration (GitHub Actions, Firebase Test Lab)
  • Documentation for running and maintaining tests
  • Team training (1–2 hour workshop)

We have developed UI tests for 15+ Android apps, average stability — 97%, largest project — 1,200 tests. Average run time for 100 tests — 15 minutes compared to 2 hours without optimization.

Timeline: from 3 to 10 days depending on app complexity. Pricing is individual. If you need help with implementation — contact us, we'll conduct an audit and propose a solution. Order development of a test suite — we guarantee >95% stability and one month of support.

Checklist for Stable UI Tests
  1. Always register IdlingResource for all background threads.
  2. Never use Thread.sleep() — use explicit waiting via IdlingResource.
  3. Ensure animations are disabled in the device test profile.
  4. Run tests on real devices via Firebase Test Lab.
  5. Clean state between tests (clearState in @Before).
  6. Use Robot pattern for readability and reusability.