This index maps a developer's symptom or API of interest to the skill that resolves it. Skim By symptom first, then By API, then By workflow phase to understand where each skill fits in the loop.
Seven sets:
compose/— 25 skills, Jetpack Compose UI testing (incl. preview-driven development, preview screenshot catalogs in CI, stability validation)fundamentals/— 5 skills, the conceptual foundationkotlin/— 1 skill, Kotlin-native test tooling (thekotlin.testmultiplatform library)jvm-tests/— 6 skills, JVM unit testsinstrumentation/— 6 skills, on-device tests (incl. Gradle Managed Devices)platform/— 1 skill, platform-shipped test tooling (migrating off the deprecatedandroid.test.*classes)adb/— 10 skills, ADB-driven device E2E
| Symptom | Skill |
|---|---|
| Don't know where to start writing tests for a new feature | fundamentals/concepts/choosing-what-to-test |
| Test pyramid feels arbitrary or upside-down (too many big tests, too few small) | fundamentals/concepts/understanding-the-testing-pyramid |
| Confused between fake / mock / stub / spy / dummy | fundamentals/doubles/picking-test-doubles |
| Tests are flaky and time-dependent — what's the strategy? | fundamentals/strategies/applying-testing-strategies |
src/sharedTest/ build is broken on AGP 7.2+ |
fundamentals/strategies/organizing-test-source-sets |
Confused about testImplementation vs androidTestImplementation vs debugImplementation |
fundamentals/strategies/organizing-test-source-sets |
Hilt @HiltAndroidTest rule ordering / @UninstallModules trouble |
fundamentals/strategies/applying-testing-strategies |
| Symptom | Skill |
|---|---|
import org.junit.* unresolved in commonTest / want one assertion API across targets |
kotlin/kotlin-test/writing-tests-with-kotlin-test |
testImplementation(kotlin("test")) — runs on JUnit but no JUnit was added; JUnit4 vs JUnit5 selection |
kotlin/kotlin-test/writing-tests-with-kotlin-test |
assertFailsWith / assertContentEquals / assertIs / @BeforeTest — how/when |
kotlin/kotlin-test/writing-tests-with-kotlin-test |
kotlin.test.assertEquals failure shows a bare AssertionError with no diff |
kotlin/kotlin-test/writing-tests-with-kotlin-test |
| Symptom | Skill |
|---|---|
@Deprecated on android.test.InstrumentationTestCase / AndroidTestCase / ActivityInstrumentationTestCase2 / ServiceTestCase / ProviderTestCase2 |
platform/legacy/migrating-from-android-test-classes |
testInstrumentationRunner is android.test.InstrumentationTestRunner; tests misbehave under AndroidJUnitRunner |
platform/legacy/migrating-from-android-test-classes |
Replace MoreAsserts / ViewAsserts / TouchUtils / android.test.mock.MockContext & friends |
platform/legacy/migrating-from-android-test-classes |
extends junit.framework.TestCase (JUnit3 shape) → JUnit4 |
platform/legacy/migrating-from-android-test-classes |
| Symptom | Skill |
|---|---|
ActivityNotFoundException: ComponentActivity at test launch |
compose/setup/configuring-test-dependencies |
Lint warning TestManifestGradleConfiguration |
compose/setup/configuring-test-dependencies |
Mixing createComposeRule v1 and v2 |
compose/setup/choosing-test-rule-vs-runtest |
| Compose test passes locally, fails in CI on Robolectric | compose/setup/setting-up-host-vs-device-tests |
| "no node matched" / "0 nodes found" in Compose | compose/debug/printing-the-semantics-tree, compose/finders/finding-nodes-by-tag-text-content |
| Error message hints "...were found in the unmerged tree" | compose/finders/finding-nodes-by-tag-text-content |
assertExists overuse where the test means "visible" |
compose/assertions/asserting-node-state-and-text |
performGesture deprecated |
compose/actions/injecting-touch-gestures |
Compose animation test flaky / InfiniteAnimationPolicy CancellationException |
compose/synchronization/testing-animations-deterministically |
mainClock.advanceTimeBy(5) only advances one frame, not 5 ms |
compose/synchronization/controlling-the-test-clock |
waitUntil timeout fires too quickly |
compose/synchronization/synchronizing-with-idle |
rememberSaveable state appears to vanish in Compose test |
compose/patterns/testing-state-restoration |
| Compose + Espresso (Dialog, IME, custom Activity) | compose/interop/testing-with-espresso-interop |
| Need accessibility checks on every Compose action | compose/debug/enabling-accessibility-checks |
| Inheriting an unfamiliar Compose test suite | compose/audit/auditing-compose-test-suite |
@Preview fails to render — needs a ViewModel / repository / Context |
compose/preview/developing-with-compose-previews |
| Previews keep going "out of date" / breaking; how to structure them | compose/preview/developing-with-compose-previews |
Want every UI state side by side without running the app (@PreviewParameter) |
compose/preview/developing-with-compose-previews |
Composable loads a Coil/Glide image and the preview is blank (LocalInspectionMode) |
compose/preview/developing-with-compose-previews |
Want @Previews screenshotted on a real device and a browsable catalog in CI |
compose/preview/capturing-preview-screenshots-in-ci |
captureAllPreviews / HotSwan / preview catalog on GitHub Pages |
compose/preview/capturing-preview-screenshots-in-ci |
| Paparazzi vs Roborazzi vs device-rendered preview screenshots — which? | compose/preview/capturing-preview-screenshots-in-ci |
| A composable became unskippable / a parameter went stable → unstable | compose/stability/validating-compose-stability |
Want CI to fail on Compose stability regressions (stabilityCheck / baseline) |
compose/stability/validating-compose-stability |
| Symptom | Skill |
|---|---|
error: device not found after USB reconnect |
adb/devices/connecting-to-devices, adb/architecture/understanding-adb-architecture |
error: more than one device/emulator |
adb/devices/connecting-to-devices |
| Set up wireless ADB on Android 11+ | adb/devices/connecting-over-wifi |
INSTALL_FAILED_USER_RESTRICTED / signing-conflict on adb install |
adb/apps/installing-and-managing-apps |
am force-stop doesn't reset the app between tests |
adb/apps/installing-and-managing-apps, adb/control/injecting-input-and-state |
am instrument returns 0 even though tests failed |
adb/tests/running-instrumented-tests-via-adb, adb/automation/scripting-adb-for-ci |
svc wifi disable is a silent no-op on API 30+ |
adb/control/injecting-input-and-state |
input text "hello world" drops everything after the space |
adb/control/injecting-input-and-state |
screencap produces a corrupt PNG |
adb/capture/capturing-screenshots-and-screenrecord |
screenrecord --time-limit 600 silently capped at 180 |
adb/capture/capturing-screenshots-and-screenrecord |
| `adb logcat | grep ...` is slow on a busy device |
| Need just the crash from logcat, not noise | adb/observability/extracting-logs-with-logcat |
adb pull /data/data/<pkg>/... denied |
adb/transfer/extracting-test-artifacts |
Pulling a binary file via adb shell run-as cat corrupts it |
adb/transfer/extracting-test-artifacts |
| Run instrumented tests across N parallel emulators | adb/automation/scripting-adb-for-ci |
adb forward vs adb reverse argument-order confusion |
adb/automation/scripting-adb-for-ci |
| API or tool | Skill |
|---|---|
kotlin.test — @Test/@BeforeTest/@AfterTest, assertEquals/assertContentEquals/assertIs/assertFailsWith/expect/fail, Asserter/AsserterContributor, kotlin("test")/kotlin-test-junit/kotlin-test-junit5 |
kotlin/kotlin-test/writing-tests-with-kotlin-test |
androidx.test:core / runner / rules / ext:junit / ext:truth / monitor |
jvm-tests/runner/configuring-junit4-on-android, instrumentation/runner/running-instrumented-tests-with-androidjunit4 |
AndroidJUnit4, InstrumentationRegistry, ApplicationProvider |
jvm-tests/runner/configuring-junit4-on-android, instrumentation/runner/running-instrumented-tests-with-androidjunit4 |
android.test.* (deprecated) — InstrumentationTestCase/AndroidTestCase/ActivityInstrumentationTestCase2/ServiceTestCase/ProviderTestCase2/InstrumentationTestRunner/MoreAsserts/ViewAsserts/TouchUtils/android.test.mock.* |
platform/legacy/migrating-from-android-test-classes |
ActivityScenario, ActivityScenarioRule |
instrumentation/scenarios/launching-activities-with-activityscenario |
FragmentScenario, launchFragmentInContainer, launchFragment |
instrumentation/scenarios/launching-fragments-with-fragmentscenario |
Espresso.onView, ViewMatchers, ViewActions, RootMatchers, IdlingResource |
instrumentation/espresso/writing-espresso-tests |
Intents.init/release/intended/intending, IntentsRule |
instrumentation/espresso/writing-espresso-tests |
RecyclerViewActions, DrawerActions, PickerActions, NavigationViewActions |
instrumentation/espresso/writing-espresso-tests |
UiDevice, BySelector, UiObject2, Until, Configurator |
instrumentation/uiautomator/cross-app-tests-with-uiautomator |
android.testOptions.managedDevices, ManagedVirtualDevice, localDevices/groups, allDevicesCheck, <device>DebugAndroidTest, ATD images |
instrumentation/managed-devices/running-tests-on-gradle-managed-devices |
Mockito, @Mock, whenever, argumentCaptor, mock-maker-inline |
jvm-tests/mocking/mocking-with-mockito |
MockK, mockk, every, coEvery, verify, coVerify, slot, mockkStatic, mockkObject, mockkConstructor |
jvm-tests/mocking/mocking-with-mockk |
runTest, TestScope, StandardTestDispatcher, UnconfinedTestDispatcher, TestCoroutineScheduler, Dispatchers.setMain |
jvm-tests/coroutines/testing-coroutines-with-runtest |
Turbine, flow.test, awaitItem, expectMostRecentItem, turbineScope, testIn, standalone Turbine<T>() (add/close), takeItem |
jvm-tests/coroutines/testing-flows-with-turbine |
Robolectric, RobolectricTestRunner, @Config, Shadows.shadowOf, ShadowLooper |
jvm-tests/robolectric/using-robolectric-correctly |
Hilt, @HiltAndroidTest, HiltAndroidRule, @TestInstallIn, @UninstallModules, @BindValue |
fundamentals/strategies/applying-testing-strategies |
createComposeRule, runComposeUiTest, ComposeTestRule, ComposeUiTest, StateRestorationTester |
compose/setup/choosing-test-rule-vs-runtest, compose/patterns/testing-state-restoration |
onNodeWithTag, onNodeWithText, onNodeWithContentDescription, onAllNodes, onRoot, useUnmergedTree |
compose/finders/finding-nodes-by-tag-text-content |
SemanticsMatcher, hasText, hasClickAction, hasScrollToKeyAction, hasParent, hasAnyAncestor, hasAnyDescendant |
compose/finders/composing-semantics-matchers |
onParent, onChildren, onSibling, onAncestors, filter, filterToOne |
compose/finders/traversing-the-semantics-tree |
assertExists, assertIsDisplayed, assertIsEnabled, assertIsOn, assertTextEquals, assertCountEquals |
compose/assertions/asserting-node-state-and-text |
assertWidthIsEqualTo, assertHeightIsAtLeast, getUnclippedBoundsInRoot, Dp.assertIsEqualTo |
compose/assertions/asserting-bounds-and-dimensions |
performClick, performScrollToIndex, performScrollToKey, performScrollToNode |
compose/actions/clicking-and-scrolling |
performTouchInput, swipe, pinch, longClick, multiTouchSwipe |
compose/actions/injecting-touch-gestures |
performMouseInput, performKeyInput, withKeyDown, MouseButton, ScrollWheel |
compose/actions/injecting-mouse-and-keyboard |
performTextInput, performTextReplacement, performImeAction |
compose/actions/entering-text |
MainTestClock, autoAdvance, advanceTimeByFrame, advanceTimeBy, advanceTimeUntil |
compose/synchronization/controlling-the-test-clock |
mainClock.autoAdvance = false, InfiniteAnimationPolicy, onAnimationFrame helper |
compose/synchronization/testing-animations-deterministically |
waitForIdle, waitUntil, runOnIdle, runOnUiThread, IdlingResource, IdlingPolicies.setMasterPolicyTimeout |
compose/synchronization/synchronizing-with-idle |
printToLog, printToString, fetchSemanticsNode |
compose/debug/printing-the-semantics-tree |
enableAccessibilityChecks, AccessibilityValidator, tryPerformAccessibilityChecks |
compose/debug/enabling-accessibility-checks |
@Preview, @PreviewParameter, PreviewParameterProvider, LocalInspectionMode, multipreview annotation classes |
compose/preview/developing-with-compose-previews |
captureAllPreviews, HotSwanPreviewActivity, @PreviewScreenshot, hotSwanCompiler { preview { … } } (Compose HotSwan) |
compose/preview/capturing-preview-screenshots-in-ci |
stabilityDump, stabilityCheck, composeStabilityAnalyzer { stabilityValidation { … } }, @IgnoreStabilityReport |
compose/stability/validating-compose-stability |
adb start-server, adb kill-server, adb reconnect, adb --version |
adb/architecture/understanding-adb-architecture |
adb devices [-l], -s, -d, -e, -t, wait-for[-TRANSPORT]-<state> |
adb/devices/connecting-to-devices |
adb pair, adb connect, adb disconnect, adb mdns check/services, adb tcpip |
adb/devices/connecting-over-wifi |
adb install [-r/-d/-t/-g], pm list / clear / grant / disable / dump-profiles |
adb/apps/installing-and-managing-apps |
adb shell am instrument -w -r -e ..., INSTRUMENTATION_STATUS_CODE, AndroidJUnitRunner status codes |
adb/tests/running-instrumented-tests-via-adb |
adb shell input tap/swipe/text/keyevent, wm size/density, settings put, cmd <service> |
adb/control/injecting-input-and-state |
adb shell screencap, adb shell screenrecord, adb exec-out |
adb/capture/capturing-screenshots-and-screenrecord |
adb logcat -b, -v threadtime/json, -T/-t, --pid, -r/-n/-f, -G/-g |
adb/observability/extracting-logs-with-logcat |
adb pull/push -z/-Z/--sync/-a, adb exec-out run-as, scoped storage |
adb/transfer/extracting-test-artifacts |
adb forward, adb reverse, parallel xargs, timeout, retry, Test Orchestrator |
adb/automation/scripting-adb-for-ci |
A typical Android testing workflow runs through five phases. Skip phases at your peril.
Build the right mental model before touching any test framework.
- fundamentals/concepts/understanding-the-testing-pyramid
- fundamentals/concepts/choosing-what-to-test
- fundamentals/doubles/picking-test-doubles
- fundamentals/strategies/applying-testing-strategies
- fundamentals/strategies/organizing-test-source-sets
Run on the JVM. Mock dependencies. Use Robolectric only when Android stubs are unavoidable.
- kotlin/kotlin-test/writing-tests-with-kotlin-test — the
kotlin.testassertion vocabulary used inside all of the below (and incommonTestfor multiplatform modules) - jvm-tests/runner/configuring-junit4-on-android
- jvm-tests/mocking/mocking-with-mockito
- jvm-tests/mocking/mocking-with-mockk
- jvm-tests/coroutines/testing-coroutines-with-runtest
- jvm-tests/coroutines/testing-flows-with-turbine
- jvm-tests/robolectric/using-robolectric-correctly
Run on an emulator or physical device. Cover the View / Fragment / Activity stack and cross-app flows.
- instrumentation/runner/running-instrumented-tests-with-androidjunit4
- instrumentation/scenarios/launching-activities-with-activityscenario
- instrumentation/scenarios/launching-fragments-with-fragmentscenario
- instrumentation/espresso/writing-espresso-tests
- instrumentation/uiautomator/cross-app-tests-with-uiautomator
- instrumentation/managed-devices/running-tests-on-gradle-managed-devices — run the above on emulators Gradle provisions/boots/tears down; reproducible in CI
- platform/legacy/migrating-from-android-test-classes — if the codebase still has
android.test.*(ActivityInstrumentationTestCase2,MoreAsserts, …), migrate it onto the above first
The richest set in this repo. Setup, finders, assertions, actions, sync, patterns, interop, debug, audit.
- Setup: compose/setup/configuring-test-dependencies, compose/setup/choosing-test-rule-vs-runtest, compose/setup/setting-up-host-vs-device-tests
- Find: compose/finders/finding-nodes-by-tag-text-content, compose/finders/composing-semantics-matchers, compose/finders/traversing-the-semantics-tree
- Assert: compose/assertions/asserting-node-state-and-text, compose/assertions/asserting-bounds-and-dimensions
- Act: compose/actions/clicking-and-scrolling, compose/actions/injecting-touch-gestures, compose/actions/injecting-mouse-and-keyboard, compose/actions/entering-text
- Synchronize: compose/synchronization/controlling-the-test-clock, compose/synchronization/testing-animations-deterministically, compose/synchronization/synchronizing-with-idle
- Patterns: compose/patterns/structuring-a-compose-test, compose/patterns/testing-lazy-lists, compose/patterns/testing-state-restoration
- Interop: compose/interop/testing-with-espresso-interop
- Debug: compose/debug/printing-the-semantics-tree, compose/debug/enabling-accessibility-checks
- Audit: compose/audit/auditing-compose-test-suite
- Preview: compose/preview/developing-with-compose-previews, compose/preview/capturing-preview-screenshots-in-ci
- Stability gate: compose/stability/validating-compose-stability
Below the test framework. Used for E2E flows, multi-device matrices, and CI orchestration.
- Architecture: adb/architecture/understanding-adb-architecture
- Connect: adb/devices/connecting-to-devices, adb/devices/connecting-over-wifi
- Manage apps: adb/apps/installing-and-managing-apps
- Run tests: adb/tests/running-instrumented-tests-via-adb
- Drive UI: adb/control/injecting-input-and-state
- Capture: adb/capture/capturing-screenshots-and-screenrecord, adb/observability/extracting-logs-with-logcat, adb/transfer/extracting-test-artifacts
- Automate: adb/automation/scripting-adb-for-ci