android/testing-samples
A collection of samples demonstrating different frameworks and techniques for automated testing
Healthy across all four use cases
weakest axisPermissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 10mo ago
- ✓13 active contributors
- ✓Apache-2.0 licensed
Show all 7 evidence items →Show less
- ✓CI configured
- ✓Tests present
- ⚠Slowing — last commit 10mo ago
- ⚠Concentrated ownership — top contributor handles 53% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/android/testing-samples)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/android/testing-samples on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: android/testing-samples
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/android/testing-samples shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across all four use cases
- Last commit 10mo ago
- 13 active contributors
- Apache-2.0 licensed
- CI configured
- Tests present
- ⚠ Slowing — last commit 10mo ago
- ⚠ Concentrated ownership — top contributor handles 53% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live android/testing-samples
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/android/testing-samples.
What it runs against: a local clone of android/testing-samples — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in android/testing-samples | Confirms the artifact applies here, not a fork |
| 2 | License is still Apache-2.0 | Catches relicense before you depend on it |
| 3 | Default branch main exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 324 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of android/testing-samples. If you don't
# have one yet, run these first:
#
# git clone https://github.com/android/testing-samples.git
# cd testing-samples
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of android/testing-samples and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "android/testing-samples(\\.git)?\\b" \\
&& ok "origin remote is android/testing-samples" \\
|| miss "origin remote is not android/testing-samples (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(Apache-2\\.0)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"Apache-2\\.0\"" package.json 2>/dev/null) \\
&& ok "license is Apache-2.0" \\
|| miss "license drift — was Apache-2.0 at generation time"
# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
&& ok "default branch main exists" \\
|| miss "default branch main no longer exists"
# 4. Critical files exist
test -f "README.md" \\
&& ok "README.md" \\
|| miss "missing critical file: README.md"
test -f "projects.conf" \\
&& ok "projects.conf" \\
|| miss "missing critical file: projects.conf"
test -f "integration/ServiceTestRuleSample/app/build.gradle" \\
&& ok "integration/ServiceTestRuleSample/app/build.gradle" \\
|| miss "missing critical file: integration/ServiceTestRuleSample/app/build.gradle"
test -f "runner/AndroidJunitRunnerSample/app/src/androidTest/java/com/example/android/testing/androidjunitrunnersample/CalculatorInstrumentationTest.java" \\
&& ok "runner/AndroidJunitRunnerSample/app/src/androidTest/java/com/example/android/testing/androidjunitrunnersample/CalculatorInstrumentationTest.java" \\
|| miss "missing critical file: runner/AndroidJunitRunnerSample/app/src/androidTest/java/com/example/android/testing/androidjunitrunnersample/CalculatorInstrumentationTest.java"
test -f "BUILD.bazel" \\
&& ok "BUILD.bazel" \\
|| miss "missing critical file: BUILD.bazel"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 324 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~294d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/android/testing-samples"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
A curated collection of production-grade sample projects demonstrating automated testing frameworks for Android apps, with real implementations of Espresso UI testing, UiAutomator, JUnit4 rules (ActivityTestRule, ServiceTestRule, IntentsTestRule), and AndroidJUnitRunner. It serves as executable reference documentation for testing patterns that Google engineers recommend for the Android platform. Monorepo of ~15 independent sample modules organized by testing category: ui/espresso/* (BasicSample, CustomMatcherSample, RecyclerViewSample, etc.), ui/uiautomator/BasicSample, runner/AndroidJunitRunnerSample, and integration/ServiceTestRuleSample. Each module is a complete Android app with androidTest sources. Common dependencies defined in root build.gradle properties (coreVersion, extJUnitVersion, runnerVersion, rulesVersion). Builds support both Gradle and Bazel (BUILD.bazel files, WORKSPACE config).
👥Who it's for
Android app developers and QA engineers who need concrete, working examples of how to write instrumented tests, UI tests, and integration tests without starting from scratch. Team leads and test architects use these samples to establish testing best practices and patterns across their organizations.
🌱Maturity & risk
Production-mature. The repo is officially maintained by Google (googlesamples organization), uses modern Android APIs (compileSdk 34, androidx.test libraries), has comprehensive CI/CD via GitHub Actions (.github/workflows/test-all.yml), Gradle/Bazel build infrastructure, and targets Android 21-34 with managed virtual devices. This is Google's official reference for Android testing.
Low risk. Google-owned repo with clear CODEOWNERS and CONTRIBUTING.md. However, Android testing frameworks evolve frequently (Espresso, androidx.test updates), so samples may lag slightly behind bleeding-edge versions. The repo is organized as isolated samples rather than a shared library, so no transitive dependency hell, but each sample's build.gradle must be kept in sync independently.
Active areas of work
Active maintenance visible in CI workflows (.github/workflows/) including gradle-wrapper-validation.yml, composescreenshot.yml, and test-all.yml. Bazel build infrastructure is actively used (bazelci/buildkite-pipeline.yml, .bazelrc config). Recent additions include ComposeScreenshot workflow. Samples are regularly updated to track androidx.test and Espresso library releases (minSdkVersion 21, targetSdkVersion 34 indicates recent Android version adoption).
🚀Get running
Clone and pick a sample: git clone https://github.com/googlesamples/android-testing-samples.git && cd android-testing-samples/ui/espresso/BasicSample && ../gradlew build. To run tests: ./gradlew connectedAndroidTest or use managed devices: ../gradlew nexusOneApi30DebugAndroidTest (from ServiceTestRuleSample/app). For Bazel: bazel test //ui/espresso/BasicSample:tests.
Daily commands:
Navigate to specific sample directory and run: ./gradlew build && ./gradlew connectedAndroidTest (requires connected emulator/device). For managed emulator: ./gradlew nexusOneApi30DebugAndroidTest. For Bazel builds: bazel test //ui/espresso/BasicSample:tests. CI uses GitHub Actions: test-all.yml runs full test matrix across samples.
🗺️Map of the codebase
README.md— Entry point documenting all sample projects and their purposes; essential for understanding repository scope and navigation.projects.conf— Configuration file that defines all sample projects included in the repository; required reading to understand structure.integration/ServiceTestRuleSample/app/build.gradle— Demonstrates managed virtual devices and test instrumentation configuration; pattern replicated across samples.runner/AndroidJunitRunnerSample/app/src/androidTest/java/com/example/android/testing/androidjunitrunnersample/CalculatorInstrumentationTest.java— Core instrumentation test example showing AndroidJUnitRunner patterns and Espresso usage.BUILD.bazel— Root Bazel build configuration; establishes multi-project build strategy used across all samples..github/workflows/test-all.yml— CI/CD pipeline that validates all samples; defines testing strategy and acceptance criteria.
🛠️How to make changes
Add a new Espresso test sample
- Create a new sample directory under ui/espresso/ (e.g., ui/espresso/NewFeatureSample) (
ui/espresso/NewFeatureSample/README.md) - Create Activity and test activity under app/src/main/java and app/src/androidTest/java following existing patterns (
ui/espresso/NewFeatureSample/app/src/androidTest/java/com/example/android/testing/NewFeatureSample/NewFeatureTest.java) - Configure app/build.gradle with AndroidJUnitRunner and Espresso dependencies, matching runner/AndroidJunitRunnerSample/app/build.gradle structure (
ui/espresso/NewFeatureSample/app/build.gradle) - Add project entry to projects.conf and declare BUILD.bazel targets (
projects.conf)
Add an integration testing sample
- Create new integration sample directory under integration/ (e.g., integration/MyServiceTestSample) (
integration/MyServiceTestSample/README.md) - Implement the service class to test under app/src/main/java (
integration/MyServiceTestSample/app/src/main/java/com/example/android/testing/MyServiceTestSample/MyService.java) - Create instrumentation test in app/src/androidTest/java using ServiceTestRule or similar integration patterns (
integration/MyServiceTestSample/app/src/androidTest/java/com/example/android/testing/MyServiceTestSample/MyServiceTest.java) - Configure build.gradle with managed virtual devices and test instrumentation runner, following integration/ServiceTestRuleSample/app/build.gradle (
integration/MyServiceTestSample/app/build.gradle)
Add a new CI workflow for testing
- Create new workflow file under .github/workflows/ (e.g., new-sample-test.yml) (
.github/workflows/new-sample-test.yml) - Define job with Android Gradle tasks referencing the sample project's gradlew androidTest command (
.github/workflows/new-sample-test.yml) - Reference the workflow in test-all.yml or create independent trigger conditions (
.github/workflows/test-all.yml)
🔧Why these technologies
- Espresso — Industry-standard UI testing framework for Android with synchronization guarantees and expressive DSL; demonstrated across multiple samples
- AndroidJUnitRunner — Official Android test runner enabling both unit and instrumentation tests on devices/emulators; foundational to all samples
- Gradle with Managed Virtual Devices — Enables automated test execution on configured emulator snapshots (e.g., nexusOneApi30); eliminates manual device provisioning
- Bazel — Polyglot build system enabling reproducible builds and fine-grained caching across the entire sample collection
- GitHub Actions — Native CI/CD integration for automated test execution on every push; demonstrates cloud-based testing patterns
⚖️Trade-offs already made
-
Multiple independent sample projects vs. monolithic mega-sample
- Why: Each sample focuses on a single testing pattern or framework, making it digestible and directly applicable
- Consequence: More projects to maintain; duplication of boilerplate (build.gradle, manifests); easier isolation and copying for developers
-
Both Gradle and Bazel build systems supported
- Why: Gradle is mainstream for Android; Bazel offers superior caching and polyglot support for large orgs
- Consequence: Maintenance burden for two build systems; flexibility for different org preferences; duplication of build logic
-
Real Android emulator (AVD) in CI vs. purely mocked tests
- Why: Instrumentation tests require actual Android runtime to verify UI and integrations accurately
- Consequence: Slower CI feedback (~5–10 min); more realistic coverage; higher infrastructure cost than unit tests alone
🚫Non-goals (don't propose these)
- This is not a production application; it is a reference library of testing patterns
- Does not provide test infrastructure or device farm services; assumes developer or CI system manages emulator provisioning
- Does not include performance benchmarking or stress testing examples (focus is on functional testing patterns)
- Not a replacement for official Android testing documentation; intended as supplementary samples
🪤Traps & gotchas
- Each sample uses
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"— if this is misconfigured, tests silently fail to run. 2. Managed devices (nexusOneApi30) require Gradle 7.0+ and AGP 7.1+; older wrapper versions will fail. 3. Espresso/androidx.test libraries have version interdependencies (espresso 3.5.1 requires androidx.test:core 1.5.0+); mismatched versions cause hard-to-debug ClassNotFoundException. 4. Some samples expect specific resource IDs (e.g., CustomMatcherSample looks for hint text on EditText) — changing resource layouts breaks tests without obvious errors. 5. intentsTestRule (IntentsBasicSample) requires AndroidManifest.xml to declare all intent recipients; missing declarations cause stub failures.
🏗️Architecture
💡Concepts to learn
- Espresso IdlingResource — Espresso synchronizes with background work (network, async tasks) via IdlingResource registration — critical for flake-free tests when testing async behavior like API calls or coroutines
- Intent Stubbing & Verification (Espresso-Intents) — Tests must verify outgoing Intents and stub incoming Intents without launching actual activities — essential for testing navigation and IPC without flaky full app launches
- ActivityTestRule / FragmentScenario Test Rules — JUnit4 rules automate activity/fragment setup and teardown, replacing manual onCreate/onDestroy handling — foundation of reliable instrumented test structure across all samples
- Custom Espresso Matchers — Extend Espresso's built-in matchers (onView, hasText, etc.) to handle app-specific properties like EditText hints or custom view attributes — avoids brittle View ID matching
- Managed Virtual Devices (AGP testOptions) — Gradle 7.1+ can spawn and manage emulator instances (e.g., Nexus One API 30) without manual setup — enables CI/CD integration without pre-configured emulator pools
- UiAutomator Framework — System-level UI testing framework that works across app boundaries — needed for testing intents to third-party apps or system dialogs that Espresso cannot reach
- AndroidJUnitRunner instrumentation context — Runs test code in app process with access to ApplicationContext, SharedPreferences, and resources — allows true integration testing vs pure unit tests
🔗Related repos
androidx/androidx— Source repo for androidx.test, androidx.test.ext, androidx.test.runner, and androidx.test.rules libraries that power all samples in this collectiongoogle/android-gradle-plugin— Upstream for Android Gradle Plugin managedDevices DSL and testOptions configuration used in sample build.gradle filesbazelbuild/bazel— Build system used alongside Gradle in this monorepo; maintains android_binary, android_instrumentation_test rulesgooglesamples/android-architecture— Companion repo showing app architecture patterns (MVVM, Clean Architecture) that these testing samples then demonstrate how to testgoogle/truth— Fluent assertion library used alongside JUnit4 assertions in many samples for more readable test failure messages
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add Compose UI Testing Sample with Screenshot Testing
The repo has a .github/workflows/composescreenshot.yml workflow file but no corresponding sample project demonstrating Compose UI testing with screenshot/screenshot comparison testing. This is a critical gap since Compose is the modern Android UI toolkit. A dedicated sample would show how to test Compose layouts, state changes, and leverage the screenshot testing workflow already configured.
- [ ] Create new directory
ui/compose/ComposeTestSample/with standard gradle structure - [ ] Implement sample Compose UI components (e.g., Counter, Form, Navigation)
- [ ] Add Compose testing dependencies (androidx.compose.ui:ui-test, androidx.compose.ui:ui-test-junit4)
- [ ] Write ComposeTestRule-based tests demonstrating findByText(), performClick(), assertIsDisplayed()
- [ ] Add screenshot comparison tests using paparazzi or similar framework referenced in composescreenshot.yml
- [ ] Create README.md explaining Compose testing patterns and how to run screenshot tests
- [ ] Update root README.md with link to new ComposeTestSample
Add Espresso DataAdapterSample Unit Tests and Integration Tests Coverage
The README references DataAdapterSample as showcasing Espresso's onData() entry point, but the file structure doesn't show this sample existing. Additionally, there's no visible test for adapter-based views (ListViews, Spinners) which are common real-world scenarios. Creating this sample with both UI instrumentation tests and isolated adapter unit tests would fill a critical testing pattern gap.
- [ ] Create
ui/espresso/DataAdapterSample/directory with gradle project structure - [ ] Implement an Activity with a ListView/RecyclerView populated by an ArrayAdapter or custom adapter
- [ ] Add androidTest tests using Espresso's onData() to interact with list items by position and data matching
- [ ] Add unit tests for the adapter logic (custom filtering, item binding) in src/test/
- [ ] Document the difference between adapter testing patterns and standard view testing
- [ ] Create README.md explaining when to use onData() vs. RecyclerView matchers (Espresso-contrib)
- [ ] Update root README.md with link and brief description of DataAdapterSample
Create Instrumentation Test Environment Setup Documentation and Bazel + Gradle Parity Sample
The repo uses both Bazel (BUILD.bazel, WORKSPACE, .bazelrc, bazelci/) and Gradle build systems, but there's no sample demonstrating how to run the same tests in both environments. The file common_defs.bzl suggests shared definitions exist but aren't documented. Creating a sample that works identically in Gradle and Bazel would help contributors understand the dual-build setup and resolve platform-specific test issues.
- [ ] Create a new sample directory
infrastructure/DualBuildSystemSample/with matching BUILD.bazel and build.gradle files - [ ] Implement identical Android test logic in both build systems
- [ ] Document dependencies configuration in both Gradle (build.gradle) and Bazel (BUILD.bazel, referencing common_defs.bzl)
- [ ] Add scripts/documentation showing:
./gradlew androidTestvsbazel test //infrastructure/DualBuildSystemSample/...:android_test - [ ] Create TESTING.md explaining which build system to use and how to troubleshoot environment-specific failures
- [ ] Reference .github/workflows/test-all.yml to show how CI runs both build systems
- [ ] Update root README.md with infrastructure section and link to DualBuildSystemSample
🌿Good first issues
- Add Espresso screenshot/assertion examples to WebBasicSample README — currently only shows navigation but doesn't demonstrate verifying WebView content loaded correctly using Espresso-web matchers
- Create a FragmentScenarioSample variant showing Fragment state restoration and savedInstanceState testing — current sample only shows basic launch, missing lifecycle edge cases
- Add MultiProcessSample documentation explaining IPC synchronization gotchas — sample exists but README lacks guidance on how multiprocess Espresso differs from single-process and when to use it
⭐Top contributors
Click to expand
Top contributors
- @brettchabot — 53 commits
- @JoseAlcerreca — 12 commits
- @utzcoz — 10 commits
- @DevDengChao — 6 commits
- [@Paige Mcauliffe](https://github.com/Paige Mcauliffe) — 4 commits
📝Recent commits
Click to expand
Recent commits
8c9df3a— Merge pull request #529 from cartland/on_push_pull_request_workflow_dispatch (cartland)c9b4ad6— Add CODEOWNERS (yrezgui)da5362a— Batch update to configure on: push, pull_request, and workflow_dispatch (cartland)c8d35be— Add workflow_dispatch trigger to workflows from GitHub (cartland)cdb134f— Merge pull request #524 from android/jose/test-all-workflow (JoseAlcerreca)46831cc— Enables KVM and more tweaks to CI (JoseAlcerreca)5712371— Update test-all.yml to accept licenses (JoseAlcerreca)bb1c517— Create test-all.yml workflow (JoseAlcerreca)75fe978— Adds renovate configuration (JoseAlcerreca)b15b99f— Add renovate.json (renovate[bot])
🔒Security observations
This Android testing samples repository demonstrates reasonable security practices overall. The codebase is primarily test-focused with minimal direct security risks. However, there are opportunities for improvement: 1) Implement stricter gradle wrapper validation, 2) Use explicit dependency versioning instead of root project variables, 3) Enable lint error checking to catch potential issues early, and 4) Document system image selection criteria. No hardcoded credentials, SQL injection risks, or obvious injection vulnerabilities were identified in the provided file structure. The repository appears to be a legitimate, community-facing testing samples project with appropriate CI/CD workflows in place.
- Medium · Gradle Wrapper Validation Missing Checksum —
integration/ServiceTestRuleSample/gradle/wrapper/gradle-wrapper.jar and similar locations. The repository contains gradle-wrapper.jar files without documented checksum validation in the build process. While there is a gradle-wrapper-validation.yml workflow, the actual checksum verification configuration is not visible in the provided file structure. Gradle wrapper files should be validated against known checksums to prevent tampering. Fix: Ensure gradle-wrapper-validation.yml properly validates wrapper checksums against the official Gradle release checksums. Document the expected checksums in the repository. - Medium · Pinned Dependencies Using Root Project Variables —
integration/ServiceTestRuleSample/app/build.gradle (dependencies section). Dependencies in build.gradle use root project variables (e.g., rootProject.coreVersion) without visible version pinning. This pattern makes it difficult to track and audit dependency versions across the codebase, potentially introducing transitive vulnerabilities. Fix: Define all dependency versions explicitly in a centralized configuration file (e.g., gradle.properties) with specific version numbers. Implement dependency version management tools like Gradle's dependency locking or use a Bill of Materials (BOM). - Low · Lint Warnings Not Enforced —
integration/ServiceTestRuleSample/app/build.gradle (lint section). The build configuration contains 'abortOnError false' in the lint configuration, which suppresses lint errors and allows the build to proceed even with potential security or quality issues detected by Android Lint. Fix: Change 'abortOnError false' to 'abortOnError true' to ensure build failures on critical lint issues. Alternatively, suppress only specific non-critical warnings while maintaining strict checking for security-related issues. - Low · Emulator Configuration with Non-AOSP ATD —
integration/ServiceTestRuleSample/app/build.gradle (testOptions.managedDevices section). While the current configuration uses 'aosp-atd' for better performance, the flexibility to use other system image sources without explicit security validation is present. Custom or untrusted system images could introduce vulnerabilities. Fix: Restrict system image sources to official AOSP ATD images. Document the rationale for the chosen image source and perform security reviews when changing it.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.