running-instrumented-tests-via-adb について
Gradle に頼らず `adb shell am instrument` で実行する方法を扱う、Android テスト自動化向けのスキルです。テスト選択、分割実行、AndroidX Test Orchestrator の設定、出力解析による失敗検出を説明します。
主な機能
- 01am instrument の直接実行
- 02クラス、メソッド、アノテーションによる選択
- 03numShards と shardIndex による分割
- 04AndroidX Test Orchestrator の設定
- 05ステータス出力の解析による失敗検出
活用シーン
- 01スクリプトから単一テストメソッドを実行
- 02複数デバイスへのテストスイートの分散
- 03並列 CI テストジョブの構築
- 04Orchestrator 引数設定の問題の調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ skydoves/ android-testing-skills --skill running-instrumented-tests-via-adbインストール後、Claude Code で/running-instrumented-tests-via-adbを入力して読み込まれたか確認します。
詳細
ADB を介したインストルメンテーションテストの実行 — Gradle を使わない am instrument
adb shell am instrument は Gradle が呼び出す基盤となるコマンドです。独自に APK を管理する CI スクリプト、多数のデバイスに対するシャーディングのファンアウト、単一のテストメソッドを対象とした高速なデバッグループには、これを直接実行するのが適切です。多くの CI スクリプトで起きる落とし穴は、-w を省略することです。その場合、終了コードに意味がなくなります。2 番目に多い落とし穴は、オーケストレーターとターゲットの関係を逆にすることです。
このスキルを使う場合
- スクリプトから単一のテストメソッドを実行したい場合:
adb shell am instrument -w -r -e class com.example.MyTest#myMethod ...。 numShards/shardIndexを使って、テストスイートを N 台のデバイスに分割したい場合。clearPackageData trueを指定して AndroidX Test Orchestrator を設定しているが、ターゲットとtargetInstrumentationの順序がわからなくなった場合。- テストが実際には失敗しているのに、
INSTRUMENTATION_STATUS_CODE: -2で失敗が示されていても$?が0となるため、CI スクリプトが成功と報告する場合。 - テストの実行前に、デバイス上のランナーにデバッガーのアタッチを待たせたい場合。
このスキルを使わない場合
- テスト対象のアプリをインストールまたはリセットしたい場合は、
../../apps/installing-and-managing-apps/SKILL.mdを使ってください。 - デバイスの選択、起動完了の待機、Wi-Fi デバッグの設定を行いたい場合は、
../../devices/connecting-to-devices/SKILL.mdと../../devices/connecting-over-wifi/SKILL.mdを使ってください。 - JUnit4/
AndroidJUnit4のテストクラス自体を書いている場合は、../../../instrumentation/runner/running-instrumented-tests-with-androidjunit4/SKILL.mdを使ってください。 - Gradle に実行を任せたい場合(
./gradlew connectedDebugAndroidTest)は、この方法でもtestInstrumentationRunnerArgumentsからインストルメンテーションランナーの引数が読み込まれます。
前提条件
- アプリの APK(
com.example.app)とテスト APK(com.example.app.test)がインストール済みであること。../../apps/installing-and-managing-apps/SKILL.mdを参照してください。 - テスト APK が
-tを許可する設定(android:testOnly="true")でビルドされていること。 - Test Orchestrator を使う場合:
androidx.test.orchestratorAPK がインストール済みであること(通常はandroidTestUtil("androidx.test:orchestrator:1.6.1")とadb install -r androidx.test.orchestrator.apkを使用します)。 - デバイスが
device状態であり、安定した実行のためにアニメーションが無効になっていること(docs/CORPUS.md§I.6 の密閉型テスト環境の設定を参照してください)。
ワークフロー
-
1. 標準的なコマンドを組み立てます。 developer.android.com/studio/test/command-line と
am helpからの引用です。adb shell am instrument -w -r \ [-e <key> <value>] ... \ <pkg>/<runner>-wは「戻る前にインストルメンテーションの完了を待つ。テストランナーには必須です。」という意味です。指定しないとシェルはすぐに戻り、$?は意味を持ちません。-rは「生の結果を出力する(指定しない場合はreport_key_streamresultをデコードする)」という意味です。CI で扱いやすい出力にするため、-wと組み合わせてください。
よく使われるランナー:
androidx.test.runner.AndroidJUnitRunner— AGP のandroidTestにおけるデフォルトです。androidx.test.orchestrator.AndroidTestOrchestrator— 各テストを独自のインストルメンテーション呼び出しで実行するラッパーです。
AndroidJUnitRunner を直接呼び出す例:
adb shell am instrument -w -r \ com.example.app.test/androidx.test.runner.AndroidJUnitRunner -
2. 選択、シャーディング、ランナーの動作には、
-e引数の表を使用します。 AndroidJUnitRunner / AndroidX Test が認識する全項目は次のとおりです。キー 値 意味 package<java_package>「完全修飾 Java パッケージ名です。このパッケージを使用するテストクラスが実行されます。」 classより優先されます。複数指定する場合はカンマで区切ります。class<fqcn>「このテストケースクラスのみを実行します。」 class<fqcn>#<method>「このメソッドのみを実行します。」区切り文字にはハッシュを使用します。 class<fqcn1>,<fqcn2>#m,...複数のセレクターを組み合わせる場合は、カンマで区切ります。 sizesmall/medium/large「 @SmallTest、@MediumTest、または@LargeTestが付与されたテストメソッドを実行します。」annotation<fqcn>このアノテーションが付与されたテストに絞り込みます。 notAnnotation<fqcn>このアノテーションが付与されたテストを除外します。 numShards<N>並列実行時のシャード総数です。 shardIndex<i>このシャードの 0 始まりのインデックスです(0 ≤ i < N)。 debugtrue「テストをデバッグモードで実行します。」各テストの前にデバッガーの接続を待機します。 logtrue「指定されたすべてのテストを読み込み、ログに記録しますが、実行はしません。」フィルターの組み合わせを確認するために使用します。 listener<fqcn>RunListenerを登録します。複数指定する場合はカンマで区切ります。filter<fqcn>カスタム JUnit Filterを登録します。runnerBuilder<fqcn>カスタムの RunnerBuilderです。disableAnalyticstrueAndroidJUnitRunner の使用状況統計を無効にします。 clearPackageDatatrueAndroidX Orchestrator 専用:テスト間で pm clearを実行します。targetInstrumentation<pkg>/<runner>AndroidX Orchestrator 専用:ラップするインストルメンテーションです。 coveragetrueJaCoCo のカバレッジを収集します。 coverageFile<path>デバイス上のカバレッジファイルの場所を上書きします。 使用例:
# 1 つのクラス。 adb shell am instrument -w -r \ -e class com.example.app.LoginTests \ com.example.app.test/androidx.test.runner.AndroidJUnitRunner # 1 つのメソッド。 adb shell am instrument -w -r \ -e class com.example.app.LoginTests#login_succeeds \ com.example.app.test/androidx.test.runner.AndroidJUnitRunner # @LargeTest のみ。 adb shell am instrument -w -r -e size large \ com.example.app.test/androidx.test.runner.AndroidJUnitRunner # 4 つのシャードのうちシャード 0(4 台のデバイスで並列実行すると速度が 4 倍になります)。 adb shell am instrument -w -r -e numShards 4 -e shardIndex 0 \ com.example.app.test/androidx.test.runner.AndroidJUnitRunner # 各テストの前にデバッガーの接続を待機します。 adb shell am instrument -w -e debug true \ com.example.app.test/androidx.test.runner.AndroidJUnitRunner # カスタム RunListener。 adb shell am instrument -w -r \ -e listener com.example.app.MyRunListener \ com.example.app.test/androidx.test.runner.AndroidJUnitRunner -
3. AndroidX Test Orchestrator を正しく設定します。 Orchestrator は各テストを個別のインストルメンテーション呼び出しで実行し、クラッシュを分離します。また、
clearPackageDataを指定すると、テスト間でアプリの状態を消去します。使用するには、次のようにします。- ターゲット(最後の位置引数)に Orchestrator を指定します:
androidx.test.orchestrator/.AndroidTestOrchestrator。 - ラップするランナーは、
-e targetInstrumentation <pkg>/<runner>を介して渡します。
adb shell am instrument -w -r \ -e clearPackageData true \ -e targetInstrumentation com.example.app.test/androidx.test.runner.AndroidJUnitRunner \ androidx.test.orchestrator/.AndroidTestOrchestratorよくあるコピー&ペーストの間違いは、2 つを逆にすることです。AndroidJUnitRunner を位置引数に指定し、Orchestrator を
targetInstrumentationに指定してしまいます。ランナーが Orchestrator で、ターゲットが AndroidJUnitRunner であるため、分かりにくいエラーが発生します。 - ターゲット(最後の位置引数)に Orchestrator を指定します:
-
4. 出力のフレーミングを確認します。 正確な形式は次のとおりです。
INSTRUMENTATION_STATUS: <key>=<value> # 各ステータスフレームのBundleエントリーごとに繰り返し INSTRUMENTATION_STATUS_CODE: <int> # 各ステータスフレームにつき1つ ... INSTRUMENTATION_RESULT: <key>=<value> # 最終結果のBundleエントリーごとに繰り返し INSTRUMENTATION_CODE: <int> # 最後に1回のみステータスコードの値(
Instrumentation.REPORT_VALUE_RESULT_*と AndroidJUnitRunner による追加分):コード 意味 1テスト開始( REPORT_VALUE_RESULT_START)。0テスト成功( REPORT_VALUE_RESULT_OK)。-1プロセスエラー/予期しない例外( REPORT_VALUE_RESULT_ERROR)。-2アサーション失敗( REPORT_VALUE_RESULT_FAILURE)。-3無視( @Ignore)— AndroidJUnitRunner による追加。-4前提条件の失敗( org.junit.AssumeViolatedException)— AndroidJUnitRunner による追加。AndroidJUnitRunner は、フレームワークの標準4値に
-3と-4を追加します。フレームワークのコードのみでは、テストごとのINSTRUMENTATION_STATUS_CODEに1、0、-1、-2だけが出力されます。最後のINSTRUMENTATION_CODE行は別の値で、Android のActivity.RESULT_*定数を使用します。実行がランナーレベルのエラーなしに完了した場合は-1(RESULT_OK)、ランナー自体でエラーが発生した場合は0(RESULT_CANCELED)です。この行もシェルの$?も、テストの成功/失敗を確実に示すものではありません。AndroidJUnitRunner は常にfinish(Activity.RESULT_OK, results)を呼び出し、AOSP のframeworks/base/cmds/am/.../Instrument.javaはrun()を無条件のSystem.exit(0)で終了します。そのため、テストが失敗しても$?は0になります。CI スクリプトでは、失敗を検出するために、標準出力からINSTRUMENTATION_STATUS_CODE: -2(失敗)とINSTRUMENTATION_STATUS_CODE: -1(エラー)の行を解析する必要があります。標準的な grep の書き方については手順 6 を参照してください。ステータスフレームごとの標準 Bundle キー(
Instrumentationリファレンスより):REPORT_KEY_NAME_CLASS— 現在のテストの完全修飾クラス名。REPORT_KEY_NAME_TEST— 現在のテストメソッド。REPORT_KEY_NUM_CURRENT— 現在のテストの1始まりのインデックス。REPORT_KEY_NUM_TOTAL— テストの総数。REPORT_KEY_STACK— 失敗時のスタックトレース。REPORT_KEY_STREAMRESULT— 整形済みストリーム出力(デフォルトではデコード済み。未加工のままにするには-rを指定します)。
-
5. シェルの終了コードを成功/失敗の判定に使わないでください。
-wを指定すると、全体が成功した場合もテストが失敗した場合も、$?は0を返します。am instrumentはSystem.exit(0)で終了し、AndroidJUnitRunner のfinish()はテスト結果にかかわらずRESULT_OKを報告するためです。-wを指定しない場合、シェルはすぐに戻るため、$?はさらに意味を持ちません。手順 6 を参照し、標準出力からINSTRUMENTATION_STATUS_CODE: -2(失敗)と-1(エラー)の行を解析する必要があります。-w -rは引き続き必要です(AGP/Gradle と Android Studio が指定して実行する待機フラグと未加工フラグです)。ただし、終了コードの判定には使いません。 -
6. CI 用に出力を解析します。 実用的な方法は2つあります。
- すばやく処理できる grep 対応の行:
adb shell am instrument -w -r ... \ | tee instrument.log grep -E '^INSTRUMENTATION_(CODE|STATUS_CODE|RESULT)' instrument.log grep '^INSTRUMENTATION_STATUS_CODE: -2' instrument.log && exit 1 - Gradle に XML を出力させる:
app/build/outputs/androidTest-results/connected/<variant>/TEST-*.xmlに出力します。JUnit XML テストレポーターとの連携(ほとんどの CI ダッシュボードで必要)には、この方法が必要です。このパスを使用するには./gradlew connectedDebugAndroidTestを実行します。2つの方法は併用できます。多くのパイプラインでは、シャーディング実行のためにam instrumentを直接実行し、その後、未加工のステータスストリームを独自に後処理して JUnit XML に変換します。
- すばやく処理できる grep 対応の行:
-
7. アニメーションを無効にし、状態をリセットして、隔離された実行を行います。
docs/CORPUS.md§I.6 に従います。adb shell settings put global window_animation_scale 0 adb shell settings put global transition_animation_scale 0 adb shell settings put global animator_duration_scale 0 adb shell pm clear com.example.app # 各実行の前に実行します。オーケストレーターがテストごとに実行しますまたは Gradle で
testOptions.animationsDisabled = trueと Test Orchestrator の-e clearPackageData trueを使用します。 -
8. シェルの終了コード伝播には制限があることに注意します。
adb shellの終了コード伝播が信頼できるのは、API 24/Platform Tools 24 以降に限られます。それ以前の組み合わせでは、adb shell <cmd>の$?に依存するスクリプトは、デバイス上で値を取得して(echo $? > /sdcard/exit)、それを pull する必要があります。最新のデバイスと Platform Tools 24 以降では、adb shell <cmd>; echo $?によりデバイス側の終了コードが想定どおり伝播します。ただし、am instrumentの場合、このコードはテスト結果にかかわらず常に0です(ステップ 5)。そのため、合否の判定には伝播は関係ありません。
パターン
パターン:誤りと正しい例 — -w なしの終了コード
# WRONG
adb shell am instrument -r \
com.example.app.test/androidx.test.runner.AndroidJUnitRunner
echo $?
# 0
# WRONG because: -w is omitted, so the adb shell returns immediately. $?
# reflects the launch, not the test outcome. Failures in the test run are
# silently lost.
# RIGHT — gate on stdout, NOT on $?
output=$(adb shell am instrument -w -r \
com.example.app.test/androidx.test.runner.AndroidJUnitRunner)
if echo "$output" | grep -qE 'INSTRUMENTATION_STATUS_CODE: -[12]$'; then
echo "FAILED"; exit 1
fi
# am instrument exits 0 even when tests fail (System.exit(0) in AOSP Instrument.java),
# so the only reliable signal is parsing INSTRUMENTATION_STATUS_CODE: -2 (failure) or -1 (error).
パターン:誤りと正しい例 — オーケストレーターのターゲット/targetInstrumentation の入れ替え
# WRONG
adb shell am instrument -w -r \
-e clearPackageData true \
-e targetInstrumentation androidx.test.orchestrator/.AndroidTestOrchestrator \
com.example.app.test/androidx.test.runner.AndroidJUnitRunner
# WRONG because: the orchestrator is the runner, not the target. AndroidJUnitRunner
# does not know how to invoke an "orchestrator target", and clearPackageData is
# not a recognised arg of AndroidJUnitRunner — only of the orchestrator.
# RIGHT
adb shell am instrument -w -r \
-e clearPackageData true \
-e targetInstrumentation com.example.app.test/androidx.test.runner.AndroidJUnitRunner \
androidx.test.orchestrator/.AndroidTestOrchestrator
# Orchestrator is the runner (last positional arg).
# AndroidJUnitRunner is the wrapped target (passed via -e targetInstrumentation).
パターン:誤りと正しい例 — 2 台のデバイス間でのシャーディング
# WRONG
adb -s emulator-5554 shell am instrument -w -r -e numShards 2 -e shardIndex 0 \
com.example.app.test/androidx.test.runner.AndroidJUnitRunner &
adb -s emulator-5554 shell am instrument -w -r -e numShards 2 -e shardIndex 1 \
com.example.app.test/androidx.test.runner.AndroidJUnitRunner &
wait
# WRONG because: both shards run on the same device (-s emulator-5554). They
# serialize against each other through am, defeating the parallelism goal of
# sharding. Worse, they may collide on the same app under test.
# RIGHT
adb -s emulator-5554 shell am instrument -w -r -e numShards 2 -e shardIndex 0 \
com.example.app.test/androidx.test.runner.AndroidJUnitRunner &
adb -s emulator-5556 shell am instrument -w -r -e numShards 2 -e shardIndex 1 \
com.example.app.test/androidx.test.runner.AndroidJUnitRunner &
wait
# One shard per device. Each device runs roughly half the suite.
パターン:誤りと正しい例 — 単一メソッドの実行
# WRONG
adb shell am instrument -w -r -e class com.example.app.LoginTests:login_succeeds \
com.example.app.test/androidx.test.runner.AndroidJUnitRunner
# WRONG because: the separator between class and method is `#`, not `:`.
# AndroidJUnitRunner treats the whole string as a class name and finds nothing.
# RIGHT
adb shell am instrument -w -r -e class com.example.app.LoginTests#login_succeeds \
com.example.app.test/androidx.test.runner.AndroidJUnitRunner
必須ルール
- CI/スクリプトから実行する場合は、シェルがランナーの完了を待つよう、
am instrumentに 必ず-wを渡します(標準出力の解析に必要です)。am instrumentはテスト結果にかかわらずSystem.exit(0)を呼び出すため、$?で判定しては いけません。 - CI で使用する場合は、
-wと-rを 必ず組み合わせます。生の出力は解析できますが、デコード済みのreport_key_streamresult形式は解析できません。 - 単一メソッドを選択する場合は、
<class>#<method>(ハッシュ区切り)を 必ず使用します。コロンやドットでは、エラーなく失敗します。 - オーケストレーターは ランナー(位置引数の最後)に指定し、AndroidJUnitRunner は
-e targetInstrumentationで ターゲットとして指定します。逆に指定しては いけません。 -wを省略した場合、am instrumentの終了コードに依存しては いけません。- Orchestrator なしで
clearPackageData trueが機能すると想定しては いけません。これは Orchestrator 専用の引数です。 - 推奨: CI で隔離されたテスト環境を確保するには、Test Orchestrator と
clearPackageData trueを使用します。これは Gradle のtestOptions.execution = "ANDROIDX_TEST_ORCHESTRATOR"に対応します。 - 推奨: 実行前にアニメーションを無効にする(
settings put global *_animation_scale 0)か、Gradle のtestOptions.animationsDisabled = trueを使用します。
検証
-
am instrumentの$?で判定するスクリプトがないこと。値は常に0です(AOSP のInstrument.javaではSystem.exit(0)、AndroidJUnitRunner ではfinish(RESULT_OK, …))。標準出力でINSTRUMENTATION_STATUS_CODE: -1(エラー)/-2(失敗)を検索して合否を判定します。 - 出力ストリームの末尾に
INSTRUMENTATION_CODE: <int>の行がちょうど 1 つあること。 - テストごとのフレームに、
INSTRUMENTATION_STATUS_CODE: 1(開始)があり、その後に0(成功)または-1/-2/-3/-4が続くこと。 - Orchestrator を使用する場合、ランナーの位置引数が
androidx.test.orchestrator/.AndroidTestOrchestratorで、-e targetInstrumentationに AndroidJUnitRunner の FQN が指定されていること。 - シャーディングを行う場合、
-e numShards N -e shardIndex iは 異なるデバイス(シャードごとに-sが異なる)で実行すること。 -
-e class <fqcn>#<method>でテストメソッドがちょうど 1 つ実行されること(INSTRUMENTATION_STATUS: numtests=1と照合します)。 - デバイス上のアニメーションスケールが
0.0であること(adb shell settings get global window_animation_scale)。
参考資料
- コマンドラインからテストを実行する(
am instrument):https://developer.android.com/studio/test/command-line - ADB ユーザーガイド(
am instrumentのフラグ一覧):https://developer.android.com/tools/adb#am - AndroidX Test Orchestrator:https://developer.android.com/training/testing/instrumented-tests/androidx-test-libraries/runner
Instrumentationリファレンス(レポートキー、ステータスコード):https://developer.android.com/reference/android/app/Instrumentation- 高度なテスト設定(Gradle Managed Devices、シャーディング):https://developer.android.com/studio/test/advanced-test-setup
tasks/research/A2-adb-shell-commands.md—-eの完全な一覧、AndroidJUnitRunner の-3/-4を含むステータスコードの値、オーケストレーターの接続方法、終了コードが返されるのは-wを指定した場合のみというルール。docs/CORPUS.md§I.5(am instrumentの呼び出し)、§I.6(独立したテスト設定)、§I.10(-wの終了コードルール)。- 関連スキル:
- 高レベルのアーキテクチャ:
../../architecture/understanding-adb-architecture/SKILL.md - デバイスの接続/起動完了の待機:
../../devices/connecting-to-devices/SKILL.md - ワイヤレス ADB:
../../devices/connecting-over-wifi/SKILL.md - アプリのインストール/データの消去/一覧表示:
../../apps/installing-and-managing-apps/SKILL.md
- 高レベルのアーキテクチャ:
- 関連分野のスキル:
AndroidJUnit4でインストルメンテーションテストを実行する:../../../instrumentation/runner/running-instrumented-tests-with-androidjunit4/SKILL.md- Android で JUnit4 を設定する:
../../../jvm-tests/runner/configuring-junit4-on-android/SKILL.md - ソースセットの戦略:
../../../fundamentals/strategies/organizing-test-source-sets/SKILL.md