Best for
- Debug or stabilize flaky Android UI tests
- Add Espresso tests for View-based UIs
- Add Compose UI tests for composables
vasilyu1983/AI-Agents-public/frameworks/shared-skills/skills/qa-testing-android/SKILL.md
Designs Android testing with Espresso, UI Automator, and Compose. Use when planning device matrices, screenshot tests, CI flows, or flake-control workflows.
Decision brief
Android testing automation with Espresso, UI Automator, Compose Testing, screenshot tests, and adaptive UI validation.
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Declared | Source record | Install path and trigger |
| Claude Code | Declared | Source record | Install path and trigger |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/vasilyu1983/AI-Agents-public --skill "frameworks/shared-skills/skills/qa-testing-android"Inspect the Agent Skill "qa-testing-android" from https://github.com/vasilyu1983/AI-Agents-public/blob/53f6cb73ea53a2646e3e7d4665062ad66f3683ac/frameworks/shared-skills/skills/qa-testing-android/SKILL.md at commit 53f6cb73ea53a2646e3e7d4665062ad66f3683ac. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
Recommended Gradle defaults for stable instrumented tests:
Pick the narrowest framework that can observe the behavior: Espresso or Compose first, UI Automator at the system boundary.
Review the “Quick Reference” section in the pinned source before continuing.
As of the 2026-08-31 deadline: new apps and app updates submitted to Google Play must target API level 36 (Android 16) or higher — submissions targeting lower are rejected in Play Console. Apps not updated at all must still target at least API level 35 (Android 15) or they becom…
As of the 2026-08-31 deadline: new apps and app updates submitted to Google Play must target API level 36 (Android 16) or higher — submissions targeting lower are rejected in Play Console. Apps not updated at all must still target at least API level 35 (Android 15) or they becom…
Permission review
The documentation includes network, browsing, or remote request actions.
Use web search or web fetch to verify current external facts, versions, pricing, deadlines, regulations, or platform behavior before final answers.Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 92/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 80 | Source | Repository attention, not individual Skill quality |
| Compatibility | 2 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Android testing automation with Espresso, UI Automator, Compose Testing, screenshot tests, and adaptive UI validation.
Core References: Android Testing Docs, Build-Managed Devices, Compose Testing, UI Automator, Screenshot Testing, Accessibility Checking
| Task | Command |
|---|---|
| List emulators | emulator -list-avds |
| Start emulator | emulator @<avd_name> |
| List devices | adb devices |
| Install APK | adb install -r <path-to-apk> |
| Run unit tests | ./gradlew test |
| Run instrumented tests (connected) | ./gradlew connectedAndroidTest |
| Run instrumented tests (GMD) | ./gradlew <device><variant>AndroidTest |
| Run screenshot tests | ./gradlew validateDebugScreenshotTest |
| List GMD tasks | `./gradlew tasks --all |
| Clear app data | adb shell pm clear <applicationId> |
| Component | Current stable | Notes |
|---|---|---|
| Android | 17 (API 37), shipped 2026-06-16; Android 16 (API 36) is the prior release | See "Google Play Target API Policy" below for what to actually target — the newest OS version is not automatically the target-API requirement |
| AGP | 9.2.x | Requires Gradle 8.11+; breaking DSL changes from 8.x |
| Robolectric | 4.16.x | Supports up to SDK 36 (Baklava); SDK 36 requires JDK 21. API 37 support is not yet released as of 2026-07-11 — an open upstream issue (robolectric/robolectric#11239, filed 2026-06-14) tracks it. Do not assume Robolectric can simulate Android 17 behavior yet; verify before relying on it for API-37-specific logic |
| UI Automator | 2.4.0-rc01 | Modern uiAutomator {} DSL; release-candidate, not yet fully stable — verify current status before pinning in a template |
| Compose Preview Screenshot Testing | 0.0.1-alpha15 | Still alpha; requires AGP 8.5+, Kotlin 2.2.10+ (raised from 1.9.20 — verify against the current release notes before relying on an older Kotlin floor), JDK 17+ |
| ATD images | API 30 only | Use standard google/aosp images for API 35/36/37 |
| Maestro | API 35/36 added 2026 Q2 | API 37 support unverified as of 2026-07-11 — check release notes before targeting Android 17 devices in Maestro Cloud |
As of the 2026-08-31 deadline: new apps and app updates submitted to Google Play must target API level 36 (Android 16) or higher — submissions targeting lower are rejected in Play Console. Apps not updated at all must still target at least API level 35 (Android 15) or they become invisible/uninstallable for new users on newer OS versions. A one-time extension to 2026-11-01 is available by request. This is a moving deadline — re-check https://developer.android.com/google/play/requirements/target-sdk before treating any specific API number as "the" requirement, since Google raises it roughly once a year.
connectedAndroidTest for local ad-hoc runs.google or aosp images.clearPackageData for instrumented suites that need strong isolation.@Preview coverage, Paparazzi or Roborazzi for JVM rendering, device-based snapshots only when hardware fidelity matters.Recommended Gradle defaults for stable instrumented tests:
android {
testOptions {
animationsDisabled = true
execution = "ANDROIDX_TEST_ORCHESTRATOR"
emulatorSnapshots {
enableForTestFailures = true
maxSnapshotsForTestFailures = 2
}
}
}
dependencies {
androidTestUtil(libs.androidx.test.orchestrator)
}
If you rely on test isolation between instrumented tests, also set runner args such as clearPackageData=true in your Gradle or CI wiring.
| Layer | Framework | Scope |
|---|---|---|
| Unit | JUnit + Mockito | JVM, no Android |
| Unit (Android) | Robolectric | JVM, simulated framework |
| UI (Views) | Espresso | Instrumented |
| UI (Compose) | Compose Testing | Instrumented |
| Adaptive UI | Espresso Device API + DeviceConfigurationOverride | Instrumented or host-assisted |
| Screenshot | Compose Preview Screenshot Testing, Paparazzi, Roborazzi | JVM or instrumented |
| System | UI Automator | Cross-app, system UI, benchmarking drivers |
testOptions { animationsDisabled = true } for instrumented tests.waitUntil, or UI Automator conditions instead of sleeps.MockWebServer or DI fakes; avoid live backends in CI.withId() for Views, testTag for Compose, and resource-id or content descriptions for UI Automator.A checklist says "use emulators for CI, real devices for release." The judgment call is which real-device signals are worth paying for:
google images; anything asserting on hardware-rendered pixels needs a non-ATD image; everything else should default to ATD for speed.Generic "flaky test" triage wastes time re-running instead of classifying. Android UI-test flakiness clusters into a small number of root-cause families — identify which one you're looking at before reaching for retries:
NoMatchingViewException or an assertion firing against a stale loading state. Root cause is almost always an unregistered IdlingResource (Espresso) or a missing waitUntil/synchronized TestDispatcher (Compose/coroutines) — see references/espresso-patterns.md for the registration trap.animationsDisabled = true), never per-test with sleeps; ad-hoc adb shell settings put global *_scale 0 inside a test body is a workaround that silently stops working if the runner changes.assertExists()/onNodeWithText() finds nothing because Material components merge child semantics into the parent node by default. Not a timing issue at all; do not "fix" it with a wait loop. See the merged-tree trap in references/compose-testing.md.clearPackageData, not by reordering tests.System.currentTimeMillis(), or unseeded random data leaking into assertions or screenshot goldens. Always mockable; the fact that it's still happening usually means a fake wasn't reused when a new screen was added.When a test is flaky, name which of the seven it is before touching the test — the fix for #1 (register/await) will not touch #4 (isolate/clear state), and applying #2's fix (disable animations) to a #5 issue (Doze) does nothing.
The classic pyramid (many unit tests, some integration, few E2E) still holds, but Compose changes where the middle layer sits:
createComposeRule() without an Activity or device at all — this is functionally a unit test even though it "looks like" a UI test. Prefer it over createAndroidComposeRule<Activity>() whenever the composable doesn't need real navigation, DI graph, or activity lifecycle.StateFlow, not through the UI). A Compose app that only has "instrumented Compose tests that also exercise the ViewModel" has recreated the old inverted pyramid with new tools.Screenshot testing has real setup and maintenance cost (goldens go stale, false positives from font/renderer drift, review burden on every intentional UI change). It is worth that cost when:
It is a poor early investment for a small team still iterating rapidly on visual design — churn in intentional goldens will dominate signal from real regressions. Start with host-side tools (Compose Preview Screenshot Testing, Paparazzi, Roborazzi) for cheap iteration; only add device-based snapshot testing (Shot, or ATD-excluded device runs) once a specific rendering-sensitive surface (WebView, Maps, camera preview, custom Canvas/GPU work) has already caused a shipped visual bug that host-side rendering could not have caught.
references/espresso-patterns.mdreferences/compose-testing.mdreferences/uiautomator.mdreferences/screenshot-testing.mdreferences/adaptive-screen-testing.mdreferences/accessibility-checks.mdid, Compose Modifier.testTag, system resource-id or content description.Thread.sleep().connectedAndroidTest or a single managed-device task before widening the matrix.@Preview-driven Compose UI states.waitUntil, onElement, or watcher-based synchronization.build/reports/androidTests/, screenshot reports, diff images, logcat, and managed-device outputs.# Screenshot
adb exec-out screencap -p > screenshot.png
# Screen recording
adb shell screenrecord /sdcard/demo.mp4
# Pull managed-device test artifacts after a local failure
adb pull /sdcard/Android/media ./device-artifacts
Preferred: build-managed devices. See references/gradle-managed-devices.md and references/android-ci-optimization.md.
# .github/workflows/android.yml
name: Android CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6 # or later — verify at actions/checkout releases
- uses: actions/setup-java@v5 # or later
with:
java-version: '17' # or later
distribution: 'temurin'
- uses: gradle/actions/setup-gradle@v5 # or later
- run: ./gradlew testDebugUnitTest <device><api>DebugAndroidTest -Pandroid.testoptions.manageddevices.emulator.gpu=swiftshader_indirect
Android testing request
-> Classify layer: unit, Robolectric, Compose, Espresso, UI Automator, screenshot
-> Choose device/API matrix from risk, analytics, and adaptive UI needs
-> Stabilize state, idling, selectors, permissions, and test data
-> Run local targeted tests before managed-device or connected-device gates
-> Capture artifacts: logs, screenshots, videos, and test reports
-> Deflake root cause before expanding CI matrix or retries
The reference guides are intentionally large; search within them instead of loading everything:
rg -n "^## " frameworks/shared-skills/skills/qa-testing-android/references/compose-testing.mdrg -n "Idling|waitUntil|Synchronization" frameworks/shared-skills/skills/qa-testing-android/references/compose-testing.mdrg -n "DisplaySizeRule|DeviceConfigurationOverride|fold" frameworks/shared-skills/skills/qa-testing-android/references/adaptive-screen-testing.mdrg -n "PreviewTest|Paparazzi|Roborazzi|Shot" frameworks/shared-skills/skills/qa-testing-android/references/screenshot-testing.mdMainDispatcherRule + TestDispatcher (runTest { }, advanceUntilIdle(), kotlinx-coroutines-test) for every ViewModel and StateFlow test — Dispatchers.setMain(UnconfinedTestDispatcher()) hides off-main crashes that only surface in production@Serializable endpoint per data class, so R8 full-mode stripping of kotlinx-serialization $serializer classes is caught before release (see software-android-runtime-debugging/references/proguard-r8-triage.md)onNodeWithTag(...).assertTextEquals(...) — not on reference equality of UI state objects, because Strong Skipping Mode means the UI may or may not receive the same instance across emissionsThread.sleep() for synchronizationStateFlow<UiState> reference equality across emissions (assertThat(state).isSameInstanceAs(previous)) — new data class instances from copy() have different references but structurally equal content; test semantic equality, not identity| Resource | Purpose |
|---|---|
| references/espresso-patterns.md | Espresso matchers and actions |
| references/compose-testing.md | Compose testing guide |
| references/uiautomator.md | UI Automator patterns for system UI and benchmarking |
| references/gradle-managed-devices.md | Managed device setup and CI |
| references/screenshot-testing.md | Visual regression testing |
| references/adaptive-screen-testing.md | Screen-size and foldable coverage |
| references/accessibility-checks.md | Accessibility checks for Espresso and Compose |
| references/test-orchestrator-patterns.md | AndroidX Test Orchestrator patterns |
| references/android-ci-optimization.md | CI pipeline optimization |
| references/modern-test-tooling.md | JUnit 5, MockK, Turbine, Robolectric, Maestro |
| data/sources.json | Curated external sources |
| Template | Purpose |
|---|---|
| assets/template-android-test-checklist.md | Stability checklist |
| Skill | Purpose |
|---|---|
| software-mobile | Android development |
| qa-testing-strategy | Test strategy |
| qa-testing-mobile | Cross-platform mobile |
| software-android-native | Native Android implementation and agent workflows |
| software-android-runtime-debugging | Build/install/launch proof and stale-build triage |
| software-android-design | Native Android visual design and Material 3 review |
Before applying this skill on a non-trivial task, read learnings.consolidated.md in this directory (and learnings.md if present).
After applying it, if you encountered a pattern worth remembering, a mistake worth preventing, or a domain fact that surprised you, append one dated bullet to learnings.md via agents-skills-feedback-loop/scripts/append_learning.py. Do not modify SKILL.md itself.
Frequently asked questions
Android testing automation with Espresso, UI Automator, Compose Testing, screenshot tests, and adaptive UI validation.
The source record exposes this install command: npx skills add https://github.com/vasilyu1983/AI-Agents-public --skill "frameworks/shared-skills/skills/qa-testing-android". Inspect the command and pinned source before running it.
The pinned source record declares support for: codex, claude code.
Static rules flagged network in the source; the page lists the matching lines and excerpts.
Alternatives
vasilyu1983/AI-Agents-public
Guides iOS testing with XCTest, XCUITest, Swift Testing, simctl, and xcresult. Use when choosing destinations, controlling flakes, or parsing test artifacts for native apps.
upex-galaxy/agentic-qa-boilerplate
Orchestrates in-sprint manual QA per ticket across Stages 1 (Planning), 2 (Execution) and 3 (Reporting). Use for user-story testing, bug retesting, and batch-sprint QA loops. Creates the PBI folder, drives session-start, runs the triage + veto + risk-score decision tree on bugs, produces the ATP + ATR + TC artifacts in the TMS, executes smoke and trifuerza (UI/API/DB) exploration, and files the final QA comment + bug reports. Triggers on: test this ticket, QA this user story, retest this bug, ve
upex-galaxy/agentic-qa-boilerplate
Walks new users through this repo's QA flow — Playwright + KATA + Allure + Xray stack, Jira QA workflow (Backlog → Shift-Left QA → Estimation → Ready For Dev → Ready For QA → In Test → QA Approved → Ready For Release → Deployed to Production), /shift-left-testing for pre-sprint AC refinement on backlog Stories, /sprint-testing for in-sprint manual QA, /test-documentation for TMS test cases, /test-automation for KATA-compliant E2E/API tests, /regression-testing for CI suite execution, /framework-
openai/skills
Build, scaffold, refactor, and troubleshoot ChatGPT Apps SDK applications that combine an MCP server and widget UI. Use when Codex needs to design tools, register UI resources, wire the MCP Apps bridge or ChatGPT compatibility APIs, apply Apps SDK metadata or CSP or domain settings, or produce a docs-aligned project scaffold. Prefer a docs-first workflow by invoking the openai-docs skill or OpenAI developer docs MCP tools before generating code.