Espresso — what it is, how it works, and how to use it

Author: IT Sectr Published: 2026-04-08 Reading time: 8 min

Espresso is a framework for automated UI testing of Android applications, developed by Google and included in AndroidX Test. Unlike instrumented tests that check isolated components, Espresso interacts with the real UI: it clicks buttons, enters text, and checks element visibility. According to Google Android Developers, Espresso provides automatic synchronization with the UI thread, eliminating the need for manual Thread.sleep().

Key Takeaways

  • Espresso — Android UI testing framework with automatic thread synchronization.
  • ViewMatcher — locates View elements on screen by ID, text, or parent hierarchy.
  • ViewAction — performs actions on elements: click, text input, swipe.
  • ViewAssertion — verifies element state: displayed, contains text, enabled.
  • Idling Resource — a mechanism to wait for asynchronous operations before checking UI.

What is Espresso?

Espresso is a library for writing automated UI tests for Android, part of Google AndroidX Test. It provides an API for finding View elements on screen, performing actions on them (click, input, swipe), and verifying their state (displayed, contains text, is enabled).

The key feature of Espresso is automatic synchronization with the application's main thread. The framework waits for all async tasks (coroutines, AsyncTask, Handler) to finish before executing the next check. This eliminates flaky tests caused by race conditions and makes UI tests stable and reliable — no test contains Thread.sleep() or polling loops.

Espresso follows the Three-Legged Dog principle — a test consists of three steps: find an element (ViewMatcher), perform an action (ViewAction), verify the result (ViewAssertion). All three steps are written in a chain of calls: onView().perform().check(). This concept makes tests predictable and easy to read — each test explicitly describes what it looks for, what it does, and what it checks.

How Espresso Works

Architecture of Espresso is based on three components: Espresso (entry point — static methods onView and onData), ViewMatchers (element search), ViewActions (actions) and ViewAssertions (checks). Internally, the framework uses Idling Resource for synchronization with the UI thread.

Basic Espresso Test

The simplest test finds a button by ID, performs a click, and checks that the text “Done” appears. All operations are synchronous from the test's perspective — Espresso guarantees that the UI thread has finished processing the event before the test continues. This is achieved through a built-in waiting mechanism: onView blocks the test execution until the UI becomes idle.

kotlin
@Test
fun buttonClick_showsSuccessText() {
    // Find button by ID and click
    onView(withId(R.id.button_submit))
        .perform(click())

    // Verify that the text “Done” is displayed
    onView(withText("Done"))
        .check(matches(isDisplayed()))
}

ActivityScenario Rule

To launch an Espresso test, ActivityScenario (AndroidX Test) is used, which creates an Activity in a specific state — running, paused, or destroyed. ActivityScenario allows testing the Activity lifecycle in addition to pure UI testing. For example, you can verify that data is preserved on screen rotation (Activity recreation) and restored after destruction.

ViewMatchers is a set of methods from the Espresso.onView class that allow finding Views on screen by various criteria: resource ID (R.id), text, hint, parent element, and hierarchy. If one matcher does not produce a unique result, matchers can be combined using allOf().

MatcherPurpose
withId(R.id.name)Search by resource ID
withText(“text”)Search by displayed text
withHint(“hint”)Search by EditText hint attribute
isDisplayed()Check that the element is visible on screen
hasSibling(matcher)Search by sibling element
allOf(m1, m2)Combine multiple matchers

Combining Matchers

If there are multiple identical elements on screen (e.g., two TextViews with different text), it is convenient to combine matchers using allOf: onView(allOf(withId(R.id.title), withText(“Hello”))). This guarantees selecting a single element. The inverse operator — not() — excludes elements from the search, and hasSibling() finds an element next to a known one.

ViewActions: Interacting with UI

ViewActions are actions that Espresso performs on the found View: click(), typeText(), clearText(), scrollTo(), swipeLeft() and others. Actions are passed to the perform() method, which can accept multiple actions in sequence.

Action Chaining

The perform() method accepts vararg ViewAction, allowing you to execute a sequence of actions on one element: clear the field, enter new text, close the keyboard, and click a button. All actions execute in the order they are listed, and Espresso guarantees that the previous action is complete before the next one starts.

kotlin
// Enter text in EditText and click button
onView(withId(R.id.edit_email))
    .perform(
        clearText(),
        typeText("user@example.com"),
        closeSoftKeyboard()
    )

onView(withId(R.id.button_login))
    .perform(click())

Checking via onData

For elements inside AdapterView (ListView, RecyclerView), the onData() method is used instead of onView. It works with adapter data rather than Views — it finds an element by model content and returns the corresponding View for further actions. onData uses hamcrest matchers to locate an element by model data fields.

ViewAssertions: Checking State

ViewAssertions verify that a View is in a specific state. The basic method — matches(matcher) — checks that the element matches the given matcher. Additionally, Espresso offers doesNotExist() (element is absent) and selectedDescendantsMatch() (checking nested elements).

Common Checks

The most frequent checks in UI tests: element is displayed (isDisplayed), element contains specific text (withText), element is enabled (isEnabled), element is not checked (isNotChecked). Each check throws a detailed exception on failure — including the View hierarchy on screen. This simplifies debugging: the error message shows which elements were actually on screen at the time of the check.

Custom ViewAssertions

If standard checks are insufficient, you can create a custom one through the ViewAssertion interface. A custom assertion receives a View and can check its state programmatically — for example, text color, padding, or the state of a custom component not exposed through standard matchers.

kotlin
// Check: TextView is displayed and contains text
onView(withId(R.id.text_welcome))
    .check(matches(isDisplayed()))
    .check(matches(withText("Welcome")))

// Check: element is NOT displayed
onView(withId(R.id.progress_bar))
    .check(doesNotExist())

Idling Resources for Async Operations

Idling Resource is Espresso's mechanism for synchronizing the test with asynchronous operations. By default, Espresso waits for Handler, AsyncTask, and coroutines (via coroutinesIdlingResource). If the application performs background work through custom threads or callback services, you must register a custom Idling Resource.

Example with Coroutines

Starting from AndroidX Test 1.4.0, Espresso supports coroutines via CoroutinesIdlingResource. The test automatically waits for all launched coroutines before performing UI checks. For more complex scenarios, CountingIdlingResource is used — a counter that increments on task start and decrements on completion.

kotlin
// Register IdlingResource for OkHttp
class OkHttpIdlingResource(
    private val client: OkHttpClient
) : IdlingResource {

    private var isIdle = true
    private var watcher: IdlingResource.ResourceCallback? = null

    override fun getName() = "OkHttp"

    override fun isIdleNow() = isIdle

    override fun registerIdleTransitionCallback(
        callback: IdlingResource.ResourceCallback
    ) {
        watcher = callback
    }
}

Setting Up Espresso in an Android Project

Integrating Espresso into an Android project is done by adding dependencies to the module-level build.gradle. Espresso is part of AndroidX Test, so it is enough to specify dependencies for the Espresso core, extensions, and JUnit integration. Tests are placed in the src/androidTest directory and run on a physical device or emulator via AndroidJUnitRunner.

Gradle Configuration

The minimal dependency set includes espresso-core (core), espresso-contrib (additional matchers for RecyclerView, Drawer, Picker) and runner (AndroidX test runner). All tests run on an emulator or physical device via Android Test Orchestrator.

kotlin
// build.gradle.kts (androidTest dependencies)
android {
    defaultConfig {
        testInstrumentationRunner =
            "androidx.test.runner.AndroidJUnitRunner"
    }
}

dependencies {
    androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
    androidTestImplementation("androidx.test.espresso:espresso-contrib:3.6.1")
    androidTestImplementation("androidx.test:runner:1.6.1")
    androidTestImplementation("androidx.test:rules:1.6.1")
}

Running Tests on CI

Espresso tests can be run via Google Android Test Orchestrator, which isolates each test in a separate process and clears state between runs. This eliminates flaky tests caused by residual data from previous tests and improves stability on CI servers. For parallel execution, sharding is used — distributing tests across multiple emulators.

Frequently Asked Questions

How is Espresso different from UI Automator?

Espresso works inside the application process and uses automatic synchronization with the UI thread. UI Automator works at the system level, can interact with other applications, but requires manual wait management.

Why is Espresso called the “three-legged dog” framework?

This is a metaphor from a Google presentation: an Espresso test stands on three pillars — ViewMatcher (find), ViewAction (act), and ViewAssertion (verify). Removing any one makes the test unstable, like a three-legged dog.

How to test RecyclerView with Espresso?

For RecyclerView, the espresso-contrib library is used with methods like onView(withId(R.id.recycler)).perform(actionOnItemAtPosition(0, click())). An alternative is onData() for AdapterView or a custom ViewAction to find an element by text inside RecyclerView. Additionally, RecyclerViewActions from espresso-contrib can be used for scrolling to an element and performing actions on it.

What is a flaky test and how does Espresso handle it?

A flaky test is a test that sometimes fails without code changes, due to race conditions or asynchronicity. Espresso solves this problem using Idling Resource — waiting for all background tasks to complete before performing checks.

Can Espresso be used for screenshot testing?

Espresso itself is not designed for screenshot tests, but it can be combined with libraries like Shot or Paparazzi. Espresso prepares the UI in the desired state, and the comparison library takes a screenshot and compares it with a reference. This approach is called visual regression testing and helps find unexpected interface changes.

Summary

  • Espresso — Android UI testing framework from Google with automatic synchronization.
  • ViewMatchers — API for finding elements by ID, text, hierarchy, and combinations.
  • ViewActions — click, typeText, scrollTo, swipe for UI interaction.
  • ViewAssertions — matches, doesNotExist for verifying element state.
  • Idling Resource — test synchronization with async operations and coroutines.
  • Three steps — onView().perform().check() = find, act, verify.
  • AndroidX Test — libraries for running instrumented tests on emulator or device.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also