Android UI Testing with Espresso: Development, Stability, CI

UI tests on Android often fail due to timing and asynchrony, turning the run into chaos. We develop stable Espresso tests using IdlingResource and Robot pattern, tailor them to your project, and integrate them into CI. Our team delivers a turnkey solution—from architecture to support—ensuring reliability and scalability.

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.

Our competencies:

Frequently Asked Questions

Latest works

  • Development of a mobile application for FEEDME
    Development of a mobile application for FEEDME
    971
  • Development of a mobile application for XOOMER
    Development of a mobile application for XOOMER
    869
  • Development of a mobile application for RHL
    Development of a mobile application for RHL
    1289
  • Development of a mobile application for ZIPPY
    Development of a mobile application for ZIPPY
    1143
  • Development of a mobile application for Affhome
    Development of a mobile application for Affhome
    1076
  • Development of a mobile application for the FLAVORS company
    Development of a mobile application for the FLAVORS company
    677

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.