Android Test

This documentation was generated with the assistance of AI. Please report any inaccuracies.

Run an Android/Kotlin project’s JUnit 4 unit test suite through the Android Gradle Plugin’s testDebugUnitTest task, scoped to whatever the caller asked for — the whole lib module, a single class, a class+method, a wildcard/package pattern, or a lib/app module path — and, only when explicitly asked and a device or emulator is actually attached, connectedAndroidTest instrumented tests too. A ui selector instead runs the UI tests of the catalog’s UI contract (see Building user interfaces). This is a read-only reporting skill: it runs tests and parses the generated JUnit XML into a compact summary, it does not fix failures or modify source/test code unless asked to as an explicit follow-up.

Purpose

iru-android-test turns a loosely-specified request ("run FooTest", "just the lib tests", "everything") into the correct ./gradlew …​ testDebugUnitTest --tests …​ invocation, resolving both the Gradle module (:lib vs. :app) and the --tests filter from a single selector argument. It exists so that running and narrowing down Android unit tests doesn’t require hand-composing Gradle --tests syntax each time, and so that a calling skill (or a human) gets back a short pass/fail report parsed from JUnit XML instead of a raw Gradle log — while also gating the (slower, environment-dependent) instrumented-test path behind an explicit ask and an attached device/emulator, so it’s never run by accident.

UI runs (ui selector)

ui: jvm|device|all, or a selector starting with the word ui (ui, ui OrderRowTest; bare ui means all), runs the UI tests (Step 4b) instead of the plain unit run:

  • JVM stage — Robolectric + Roborazzi component/screen tests through testDebugUnitTest, no emulator needed; they capture through the module’s testing/UiScreenshots.kt helper.

  • Device stage — instrumented Espresso/Compose flow tests through connectedDebugAndroidTest with the uiAccessibility runner argument, only when adb devices lists an attached device; otherwise the stage is reported unverified (never passed). Captures are collected from AGP’s additional test output (or adb pull) into the same layout.

  • UI_SCREENSHOTS_DIR is exported (default <repo root>/.ui-screenshots) and cleared only at the start of a full, unscoped UI run; a scoped run adds to it.

  • The accessibility level (enforce/report/off/none) comes from .ui-quality.yml; a bundled parser validates every capture name against <kind>/<Id>/<state>--<direction>--<theme>[--<variant>…].png, counts captures per kind and Accessibility Test Framework results by check id.

Kotlin Multiplatform projects are out of scope: when the Kotlin Multiplatform plugin is applied anywhere in the build (a KMP app’s Android host module included), the skill stops and hands the request to KMP Test instead.

Plugin

Shipped by the iru-android plugin of the ai-catalog marketplace (install with /plugin install iru-android@ai-catalog; see Plugins).

Invocation

/iru-android-test
/iru-android-test [selector]

Inputs

Argument Required Description Default

selector

No

A class name (FooTest), a class+method (FooTest#testSomething), a wildcard/package pattern (*ServiceTest), or a path under a module’s src/test/src/androidTest tree (lib/src/test/java/…​/FooTest.kt, app/src/test/…​). The module (:lib by default, :app when the path says so) and the --tests pattern are both derived from it. If the calling skill/prompt instead passes explicit key: value lines (e.g. module: app), those are honored directly and the module/pattern inference below is skipped.

None — omitting it runs the whole lib module unscoped.

Selector-to-module mapping (Step 1):

Selector shape Module

No selector

:lib (default)

Path containing lib/src/test or lib/src/androidTest

:lib

Path containing app/src/test or app/src/androidTest

:app

Bare class name/pattern, no path

Search /src/test/*/<Name>*.{kt,java} under the repo root — exactly one module matches → use it; several match → ask which, or default to lib and say so explicitly; none match → still default to lib and let Gradle’s own "No tests found" surface the typo rather than guessing further

Selector-to---tests mapping (Step 2):

What the user wants --tests value

Everything in the module

(omit --tests)

One class

--tests '*.FooTest' (or the fully-qualified name if known)

One method

--tests '*.FooTest.testSomething'

Several classes/methods

Repeat --tests once per pattern — Gradle ANDs multiple --tests flags as independent inclusions, unlike Maven Surefire’s single comma-separated -Dtest value

Wildcard/package pattern

--tests 'com.example.pkg.*'

An unmatched --tests pattern fails the whole build (Gradle exits non-zero with "No tests found for given includes"), which the skill reports as "no matching tests for <pattern>`" — never as "0 passed, 0 failed". The prompt (not necessarily the selector) can also ask for instrumented/on-device tests; those only run when a device/emulator is actually attached (`adb devices lists a device-state line), and the skill says explicitly when it skips them.

Outputs

  • A plain-text summary in the conversation — never a written file.

  • Total tests run, passed, failed, and skipped counts (parsed from <module>/build/test-results/testDebugUnitTest/*.xml).

  • If everything passed: a brief confirmation, without a per-test list.

  • If there are failures/errors: each failing test’s classname#name, its assertion/exception message, and the first stack-trace line, pointing to the JUnit XML file for the full trace instead of pasting it.

  • If instrumented tests were requested but skipped (no device attached): an explicit note, not a silent omission.

  • For a UI run: the JVM and device stages reported separately (pass/fail counts, or unverified with the reason), screenshot counts per kind (component/screen/flow) under .ui-screenshots/, malformed capture names, accessibility results per check id and the tests they failed, the .ui-quality.yml level applied, and whether the directory was cleared or added to — never the screenshots, logcat or accessibility dumps.

  • No source or test code is ever modified.

Execution flow

flowchart TD A["Start /iru-android-test\n(optional selector)"] --> B{"Selector given?"} A -. "KMP plugin applied" .-> KMP["Stop: hand off to iru-kmp-test"] B -- no --> C["Module = :lib, no --tests filter"] B -- yes --> D["Resolve module\n(path prefix, or search\nsrc/test for bare class name)"] D --> E["Map selector to\n--tests pattern"] E --> F["Check prerequisites:\nJAVA_HOME 17+, ANDROID_HOME/\nlocal.properties, ./gradlew present"] C --> F F --> U{"ui selector?"} U -- yes --> U1["Step 4b: set UI_SCREENSHOTS_DIR, read\n.ui-quality.yml; JVM stage (Roborazzi);\ndevice stage only if adb device attached\n(else unverified); validate names +\ncount accessibility results"] U -- no --> G["Run ./gradlew :&lt;module&gt;:testDebugUnitTest\n[--tests '&lt;pattern&gt;'] [--offline]"] G --> H{"Instrumented tests\nexplicitly requested?"} H -- yes --> I{"Device/emulator\nattached (adb devices)?"} I -- yes --> J["Run ./gradlew :&lt;module&gt;:connectedDebugAndroidTest"] I -- no --> K["Skip instrumented run,\nnote it explicitly"] H -- no --> L["Parse JUnit XML results\n(test-results/testDebugUnitTest/*.xml)"] J --> L U1 --> L K --> L L --> M{"Build failed before\nproducing XML?"} M -- yes --> N["Report the build failure\n(compile error or unmatched --tests)"] M -- no --> O{"Failures or errors?"} O -- yes --> P["List failing classname#name,\nmessage, first stack-trace line"] O -- no --> Q["Report: all passed, briefly"]

Dependencies

Invokes

None — iru-android-test is a leaf skill; it only runs Gradle/adb commands and parses the resulting JUnit XML, it does not call any other skill in this catalog.

It stops and redirects Kotlin Multiplatform projects to KMP Test (a hand-off, not an invocation).

Invoked by

  • Android Code One Task Group — Step 3.3, via the iru-gate-runner agent, to run the scoped unit tests for every class the task-group bucket affected, per Gradle module.

  • Android Generate All Tests — Step 7, to run the newly-generated tests it wrote and confirm they pass.

  • React Native Code One Task Group — Step 3, only when the app’s android/ directory can be targeted as the Gradle root (otherwise it runs testDebugUnitTest directly), to run the JVM unit tests of the React Native app’s Android side.

Source

SKILL.md on GitHub — the file this page was generated from.