How to run Espresso tests on Gradle managed devices

Gradle managed devices let an Android project declare the emulator target used for instrumentation tests instead of relying on whatever device happens to be attached. For Espresso suites, that keeps the screen size, API level, and system image close to the same across developer machines and CI runners.

The managed device definition lives in the app module's Android Gradle Plugin configuration. Gradle uses that definition to create a named Android test task, starts or provisions the emulator, installs the app and test APKs, runs the instrumentation package, and stores the test artifacts under the module build directory.

Use one small virtual device first, then add device groups or extra API levels after the single-device task passes. A managed device still needs the Android SDK, a compatible system image, and enough CPU, memory, and emulator support on the machine that runs Gradle.

Steps to run Espresso tests on Gradle managed devices:

  1. Confirm the app module already has Espresso dependencies and the AndroidJUnitRunner configured.

    Managed devices run the instrumentation tests that the project already exposes. Add the test runner and AndroidX Test dependencies before declaring the device.
    Related: How to configure an Android project for Espresso

  2. Add a managed virtual device to the app module.
    build.gradle.kts
    android {
        testOptions {
            managedDevices {
                localDevices {
                    create("pixel2api30") {
                        device = "Pixel 2"
                        apiLevel = 30
                        systemImageSource = "aosp-atd"
                    }
                }
            }
        }
    }

    Automated Test Device images are optimized for instrumentation testing. The aosp-atd image is a good first target when the app does not need Google APIs; use google-atd when the tested flow depends on Google services.

  3. Run the generated Gradle task for the managed device and build variant.
    $ ./gradlew :app:pixel2api30DebugAndroidTest
    > Task :app:pixel2api30DebugAndroidTest
    Starting 3 tests on pixel2api30
    com.example.app.LoginTest > signsInWithValidUser PASSED
    com.example.app.SettingsTest > opensNotifications PASSED
    com.example.app.CartTest > showsEmptyCart PASSED
    
    Test execution completed. See the report at:
    file:///repo/app/build/reports/androidTests/managedDevice/pixel2api30/index.html
    
    BUILD SUCCESSFUL in 2m 14s

    Task names combine the managed-device name, build variant, and AndroidTest. On CI runners without emulator graphics acceleration, keep the same task and add -Pandroid.testoptions.manageddevices.emulator.gpu=swiftshader_indirect to the Gradle invocation.

  4. Confirm that Gradle wrote the managed-device HTML report.
    $ find app/build/reports/androidTests/managedDevice -name index.html -print
    app/build/reports/androidTests/managedDevice/pixel2api30/index.html

    The Android Gradle Plugin prints the report path at the end of the test task. Use the printed path when flavors, variants, or device groups add extra directories.

  5. Confirm that the managed-device result directory contains JUnit XML for CI.
    $ find app/build/outputs/androidTest-results/managedDevice -name 'TEST-*.xml' -print
    app/build/outputs/androidTest-results/managedDevice/pixel2api30/TEST-pixel2api30.xml

    Archive both app/build/reports/androidTests/managedDevice and app/build/outputs/androidTest-results/managedDevice when the CI system needs a human report and machine-readable test results.
    Related: How to generate a JUnit report from Espresso tests