| name | running-tests-on-gradle-managed-devices |
| description | Use this skill to run instrumented Android tests on Gradle Managed Devices (GMD) — emulators that Gradle provisions, boots, runs tests on, and tears down, so CI and every developer get the same device without managing an emulator by hand. Covers the `android.testOptions.managedDevices { localDevices { create(...) { device; apiLevel; systemImageSource } } }` DSL, `ManagedVirtualDevice`, device `groups`, the generated tasks (`<deviceName>DebugAndroidTest`, `<groupName>GroupDebugAndroidTest`, `allDevicesCheck`), Automated Test Device (ATD) images (`aosp-atd`/`google-atd`) for lighter headless CI, test sharding across managed-device copies, where the HTML report and extra outputs land, and how this differs from `connectedAndroidTest` and `adb shell am instrument`. Use when the user mentions "Gradle Managed Devices", "managedDevices", "allDevicesCheck", "ManagedVirtualDevice", "pixel2api30DebugAndroidTest", "ATD image", "automated test device", or "reproducible emulator for tests". |
| license | Apache-2.0. See LICENSE for complete terms. |
| metadata | {"author":"Jaewoong Eum (skydoves)","keywords":["android-testing","instrumented-tests","gradle-managed-devices","managedDevices","allDevicesCheck","ManagedVirtualDevice","atd-image","ci-cd","test-sharding","emulator"]} |
Running Tests On Gradle Managed Devices — Gradle Owns The Emulator
Gradle Managed Devices (GMD) move the emulator into the build: you declare devices in build.gradle.kts, and ./gradlew <device>DebugAndroidTest downloads the system image, boots a fresh emulator, runs androidTest, collects results, and shuts it down. The payoff is reproducibility — CI and every machine run the exact same device — and no hand-managed emulators. This skill covers the DSL, the generated tasks, ATD images for cheap CI, sharding, and where GMD sits relative to connectedAndroidTest and raw am instrument (see ../../../adb/tests/running-instrumented-tests-via-adb/SKILL.md).
When to use this skill
- The user wants instrumented tests to run on a defined, reproducible emulator in CI without
adb-managing a device.
- The user mentions
managedDevices, allDevicesCheck, ManagedVirtualDevice, or a generated task like pixel2api30DebugAndroidTest.
- The user wants the test matrix (which API levels / form factors) to live in version control, not in someone's local AVD list.
- CI emulator runs are slow/flaky and the user asks about ATD ("Automated Test Device") images.
- The user wants to shard a slow instrumented suite across several emulator copies.
When NOT to use this skill
- The user wants to run tests on a physical device or an emulator they already have running — that is
./gradlew connectedAndroidTest / connectedDebugAndroidTest; GMD is for emulators Gradle creates.
- The user is invoking the runner directly without Gradle (
adb shell am instrument -w -r …, sharding via -e numShards, Test Orchestrator wiring) — use ../../../adb/tests/running-instrumented-tests-via-adb/SKILL.md and ../../../adb/automation/scripting-adb-for-ci/SKILL.md.
- The user is choosing the
AndroidJUnit4 runner / writing the test class itself — use ../../runner/running-instrumented-tests-with-androidjunit4/SKILL.md.
- The user wants Compose UI tests specifically — those still run as instrumented tests; GMD just hosts them. See
../../../compose/setup/setting-up-host-vs-device-tests/SKILL.md for host-vs-device choice first.
Prerequisites
- Android Gradle Plugin with GMD support (the stable
managedDevices DSL; older AGP exposed parts of it under android.testOptions.managedDevices experimentally — check the docs for your AGP version).
androidTest set up normally: testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner", androidTestImplementation dependencies. GMD changes where tests run, not how they are written.
- Disk space and a working KVM/HAXM/hypervisor on the build machine (CI runners need KVM enabled) — GMD launches a real emulator.
- License acceptance for the system images GMD downloads (
sdkmanager --licenses, or accepted in CI setup).
Workflow
android {
testOptions {
managedDevices {
localDevices {
create("pixel6api34") {
device = "Pixel 6"
apiLevel = 34
systemImageSource = "aosp"
}
create("pixel2api30") {
device = "Pixel 2"
apiLevel = 30
systemImageSource = "aosp-atd"
}
}
groups {
create("ciMatrix") {
targetDevices.add(localDevices["pixel6api34"])
targetDevices.add(localDevices["pixel2api30"])
}
}
}
}
}
Each create("name") { … } is a ManagedVirtualDevice: device is a Device Manager profile, apiLevel the system image API, systemImageSource which image family. Use require64Bit = true if you need to force the 64-bit image. A group bundles devices so one task runs the suite across all of them.
./gradlew pixel6api34DebugAndroidTest
./gradlew ciMatrixGroupDebugAndroidTest
./gradlew allDevicesCheck
Variant naming follows the build variant (…DebugAndroidTest, …ReleaseAndroidTest, flavor-prefixed if you have flavors). These tasks: download the system image if missing, boot a fresh emulator, install the app + test APKs, run the suite, write results, and tear the emulator down — no adb choreography from you.
jobs:
androidTest:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with: { distribution: zulu, java-version: 17 }
- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules && sudo udevadm trigger --name-match=kvm
- run: ./gradlew pixel2api30DebugAndroidTest
- uses: actions/upload-artifact@v4
if: always()
with:
name: androidTest-report
path: |
app/build/reports/androidTests/managedDevice/
app/build/outputs/managed_device_android_test_additional_output/
The emulator is headless by default. (If you need to watch it locally — debugging a UI test — see the GMD docs for the option to show the emulator window; it is off in CI.)
Patterns
Pattern: using connectedAndroidTest in CI and managing the emulator by hand
- run: |
avdmanager create avd -n ci -k 'system-images;android-30;default;x86_64'
emulator -avd ci -no-window -no-snapshot &
adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done'
./gradlew connectedDebugAndroidTest
android { testOptions { managedDevices { localDevices { create("ciDevice") {
device = "Pixel 2"; apiLevel = 30; systemImageSource = "aosp-atd"
} } } } }
Pattern: full system image in CI when ATD would do
create("ciDevice") { device = "Pixel 6"; apiLevel = 34; systemImageSource = "google_apis_playstore" }
create("ciDevice") { device = "Pixel 6"; apiLevel = 34; systemImageSource = "google-atd" }
Pattern: expecting GMD task $? to be the only signal
./gradlew allDevicesCheck && echo "all good"
- run: ./gradlew allDevicesCheck
- uses: actions/upload-artifact@v4
if: always()
with: { name: androidTest-report, path: app/build/reports/androidTests/managedDevice/ }
Mandatory rules
- MUST declare managed devices in
android.testOptions.managedDevices in version control — the test device matrix is a build input, not a per-machine AVD.
- MUST prefer ATD images (
aosp-atd / google-atd) for CI; use a full or Play Store image only when a test needs GMS / Play Store behavior.
- MUST enable KVM (or the platform hypervisor) on CI runners and accept SDK licenses before invoking a GMD task — otherwise the emulator never boots.
- MUST archive
app/build/reports/androidTests/managedDevice/ (and …/managed_device_android_test_additional_output/) on every run, pass or fail, so failures are diagnosable.
- MUST NOT hand-script
avdmanager/emulator/adb wait-for-device and then run connectedAndroidTest when GMD applies — GMD owns provisioning, boot, and teardown.
- MUST NOT assume a managed-device task's exit code distinguishes test failures from infra failures; read the HTML report.
- PREFERRED: group devices (
groups { create("ciMatrix") { … } }) and run ciMatrixGroupDebugAndroidTest so the matrix is one task; use allDevicesCheck for the full sweep.
- PREFERRED: shard slow suites with the documented
numManagedDeviceShards property + --max-concurrent-shards rather than splitting the suite manually.
Verification
References
- developer.android.com/studio/test/gradle-managed-devices — the
managedDevices / localDevices DSL, ManagedVirtualDevice (device, apiLevel, systemImageSource, require64Bit), device groups, the generated <device>…AndroidTest / <group>Group…AndroidTest / allDevicesCheck tasks, ATD images, sharding (numManagedDeviceShards, --max-concurrent-shards), report/output locations, and showing the emulator window.
- developer.android.com/studio/test/advanced-test-setup —
testOptions.animationsDisabled, Test Orchestrator, sharding context that also applies to GMD runs.
- developer.android.com/training/testing/instrumented-tests — instrumented test fundamentals; GMD is one execution environment for them.
- developer.android.com/tools/adb —
adb and am instrument, for the lower-level alternative when GMD is not in play.
- Sibling skill:
../../runner/running-instrumented-tests-with-androidjunit4/SKILL.md — the AndroidJUnit4 runner the tests use, regardless of where they execute.
- Cross-set:
../../../adb/tests/running-instrumented-tests-via-adb/SKILL.md — running the same tests via adb shell am instrument -w -r without Gradle.
- Cross-set:
../../../adb/automation/scripting-adb-for-ci/SKILL.md — CI bash idioms, sharding via -e numShards, Test Orchestrator wiring, capture-on-failure.
- Cross-set:
../../../compose/preview/capturing-preview-screenshots-in-ci/SKILL.md — uses a managed device (or android-emulator-runner) to render @Preview screenshots in CI.