mcpField
R
running-instrumented-tests-with-androidjunit4

running-instrumented-tests-with-androidjunit4

github.com/skydoves测试与质量33965 次安装22 周前更新
代码仓库

关于 running-instrumented-tests-with-androidjunit4

此技能指南整理 AndroidJUnit4 仪器测试的依赖、Runner 配置、注解和规则,帮助 Android 开发者排查测试启动及配置问题。内容涵盖 Test Orchestrator、运行参数、权限授予和动画控制。

主要功能

  1. 01配置 AndroidX Test 依赖
  2. 02设置仪器测试 Runner
  3. 03使用非弃用 AndroidJUnit4
  4. 04读取仪器运行参数
  5. 05配置 Test Orchestrator
  6. 06授予权限并关闭动画

适用场景

  1. 01修复仪器测试 Runner 找不到的问题
  2. 02让 Test Orchestrator 按测试隔离进程
  3. 03按注解或参数筛选测试

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/skydoves/android-testing-skills --skill running-instrumented-tests-with-androidjunit4

安装后在 Claude Code 中输入/running-instrumented-tests-with-androidjunit4确认已加载。

详细介绍

使用 AndroidJUnit4 运行插桩测试——正确配置运行器栈

运行器栈配置错误时,插桩测试会在任何断言执行前失败:导入已弃用的 AndroidJUnit4,将 orchestrator 配置在 androidTestImplementation 而不是 androidTestUtil 上,或使用已弃用的 androidx.test.InstrumentationRegistry 而不是 androidx.test.platform.app.InstrumentationRegistry。此技能涵盖准确的依赖项矩阵、运行器 Gradle 配置,以及相关注解和规则。

何时使用此技能

  • IDE 将 androidx.test.runner.AndroidJUnit4 标记为 @Deprecated,或测试可以编译,但会发出弃用警告。
  • am instrument 返回 INSTRUMENTATION_FAILED: <pkg>/androidx.test.runner.AndroidJUnitRunner 或 Test runner not found。
  • Gradle 中已配置 Test Orchestrator,但测试仍共享同一个插桩进程(orchestrator 被静默禁用)。
  • 用户通过 am instrument 传入 -e size small / -e package com.foo,但测试忽略了这些参数——RunnerArgs 未接入。
  • 测试需要 ACCESS_FINE_LOCATION / RECORD_AUDIO 等权限,而用户正通过 adb 手动授予权限。
  • 用户想跳过低于 API 26 的测试,并正在查找相应注解。

何时不要使用此技能

  • 用户正在启动 Activity 或驱动生命周期——请参阅 ../../scenarios/launching-activities-with-activityscenario/SKILL.md。
  • 用户正在与 Views 交互——请参阅 ../../espresso/writing-espresso-tests/SKILL.md。
  • 用户正在驱动系统 UI 或其他应用——请参阅 ../../uiautomator/cross-app-tests-with-uiautomator/SKILL.md。
  • 用户运行的是 JVM(Robolectric / 纯 JUnit)测试——请参阅 ../../../jvm-tests/runner/configuring-junit4-on-android/SKILL.md。
  • 用户正在决定要测试什么——请参阅 ../../../fundamentals/strategies/applying-testing-strategies/SKILL.md。

前提条件

  • Android Gradle Plugin 模块(com.android.application 或 com.android.library)。
  • 磁盘上有 src/androidTest/ 源代码集(应能解析 ./gradlew :module:connectedDebugAndroidTest)。
  • 模拟器或实体设备(或 Firebase Test Lab / 托管设备)——运行器无法在 JVM 上执行。
  • 测试类路径中包含 JUnit 4(junit:junit:4.13.2);JUnit 5 需要 Vintage 引擎桥接,不在本文范围内。

工作流

  • 1. 在 androidTestImplementation 上固定 AndroidX Test 构件版本。 根据 docs/CORPUS.md H.1,这些是规范的 2026 GA 版本:
dependencies {
    androidTestImplementation("androidx.test:core:1.7.0")
    androidTestImplementation("androidx.test:runner:1.7.0")
    androidTestImplementation("androidx.test:rules:1.7.0")
    androidTestImplementation("androidx.test.ext:junit:1.3.0")
    androidTestImplementation("androidx.test.ext:junit-ktx:1.3.0")
    androidTestImplementation("androidx.test.ext:truth:1.7.0")
}

androidx.test.ext:junit 是提供未弃用的 AndroidJUnit4 运行器和未弃用的 ActivityScenarioRule 的构件。androidx.test:runner 仍提供旧版 AndroidJUnit4(已弃用)——切勿从那里导入。请参阅 tasks/research/R1-androidx-test-core.md 第 481-490 行。

  • 2. 在模块的 android.defaultConfig 中设置 testInstrumentationRunner。 运行器的 FQCN 是 androidx.test.runner.AndroidJUnitRunner——请注意,这是运行器的类,不同于已弃用的 AndroidJUnit4 测试运行器注解。摘自 tasks/research/R1-androidx-test-core.md 第 270-289 行:
android {
    defaultConfig {
        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        // Optional: pass `-e key value` args to every run via Gradle
        testInstrumentationRunnerArguments["clearPackageData"] = "true"
    }
    testOptions {
        animationsDisabled = true                  // hermetic — see Pattern below
    }
}
  • 3. 使用来自 androidx.test.ext.junit.runners 的 @RunWith(AndroidJUnit4::class) 为测试类添加注解。 这是唯一正确的导入方式:
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class LoginActivityTest {
    @Test fun loginSucceeds() { /* ... */ }
}

已弃用的 androidx.test.runner.AndroidJUnit4 在 runner-1.7.0/androidx/test/runner/AndroidJUnit4.java:43-44 标记为 @Deprecated。它在运行时仍然有效,但 IDE 会发出警告,代码自动转换工具也会替换它。

  • 4. 通过 androidx.test.platform.app.InstrumentationRegistry 读取插桩参数。 旧版 androidx.test.InstrumentationRegistry(不含 platform.app)已标记为 @Deprecated,并带有指向规范类的 @InlineMe 注解——请参阅 tasks/research/R1-androidx-test-core.md 第 142-153 行:
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.core.app.ApplicationProvider

val instrumentation = InstrumentationRegistry.getInstrumentation()
val args: Bundle = InstrumentationRegistry.getArguments()       // -e key value extras
val targetContext: Context = ApplicationProvider.getApplicationContext()
val testContext: Context = instrumentation.context             // package context of the test APK

InstrumentationRegistry.getArguments() 会公开 am instrument -e key value 中的 -e 键值对,例如 -e size small -e numShards 4。受支持键的规范列表位于 runner-1.7.0/androidx/test/internal/runner/RunnerArgs.java 第 54-99 行(参见 tasks/research/R1-androidx-test-core.md 第 295-336 行)。

  • 5. 使用 androidx.test.filters 中的 @SmallTest / @MediumTest / @LargeTest 标注测试。 配合 am instrument -e size small 使用,可按速度范围筛选测试。根据该包的 KDoc:SmallTest ≤200ms,MediumTest ≤1000ms,LargeTest >1000ms。同类注解还有:@FlakyTest、@RequiresDevice、@Suppress。
import androidx.test.filters.SmallTest
import androidx.test.filters.SdkSuppress

@RunWith(AndroidJUnit4::class)
@SmallTest
class LoginViewModelTest { /* ... */ }

@Test
@SdkSuppress(minSdkVersion = 26)
fun usesApi26OnlyApi() { /* ... */ }

@SdkSuppress(minSdkVersion, maxSdkVersion, excludedSdks, codeName) 位于 runner-1.7.0/androidx/test/filters/SdkSuppress.java:39-58。超出 API 范围的测试会被跳过(假设失败,退出代码为 -4),而非失败。

  • 6. 将 Test Orchestrator 添加到 androidTestUtil,而非 androidTestImplementation。 这是 runner 堆栈中最常见的单项配置错误。Test Orchestrator 会在各自独立的插桩进程中运行每个 @Test,因此崩溃只会影响该测试;此外,还可选择在测试之间设置 clearPackageData:
dependencies {
    androidTestUtil("androidx.test:orchestrator:1.6.1")  // installs orchestrator APK on device
    androidTestUtil("androidx.test:services:1.6.0")      // required for clearPackageData
}

android {
    testOptions {
        execution = "ANDROIDX_TEST_ORCHESTRATOR"
    }
    defaultConfig {
        testInstrumentationRunnerArguments["clearPackageData"] = "true"
    }
}

androidTestUtil 是一种特殊的 AGP 配置,会在运行测试前于设备上安装辅助 APK。将 orchestrator 放入 androidTestImplementation 虽然可以编译,但不会有任何提示且不执行任何操作,测试仍会在单个共享进程中运行。参见 tasks/research/R1-androidx-test-core.md 第 683-687 行。

  • 7. 使用 GrantPermissionRule.grant(...) 授予运行时权限,而非使用 adb shell pm grant。 该规则随 androidx.test:rules 一同提供,并在测试类运行期间授予权限:
import androidx.test.rule.GrantPermissionRule
import org.junit.Rule

@get:Rule
val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(
    android.Manifest.permission.ACCESS_FINE_LOCATION,
    android.Manifest.permission.RECORD_AUDIO,
)

该规则实现在 rules-1.7.0/androidx/test/rule/GrantPermissionRule.java 中。在 API 23+ 上,它会使用 UiAutomation.grantRuntimePermission;在较旧的设备上,权限必须已在清单中授予(此规则不执行任何操作)。

  • 8. 在无外部干扰的运行中禁用动画。 testOptions { animationsDisabled = true } 会在测试运行期间将 window_animation_scale、transition_animation_scale 和 animator_duration_scale 设为 0,随后在清理时恢复之前的值。否则,点按对焦、波纹效果和 Activity 转场都可能与断言发生竞争。

  • 9. (可选)使用 @Rule(order = N) 对多个 @Rule 排序。 顺序号较小的规则会先作为外层规则执行。Hilt 要求将 @HiltAndroidRule 设为 order = 0:

@get:Rule(order = 0) val hiltRule = HiltAndroidRule(this)
@get:Rule(order = 1) val activityRule = ActivityScenarioRule(LoginActivity::class.java)
  • 10. 运行。 在主机上执行:
./gradlew :app:connectedDebugAndroidTest
# or via adb directly:
adb shell am instrument -w -r \
  -e size small \
  -e clearPackageData true \
  com.example.app.test/androidx.test.runner.AndroidJUnitRunner

要让退出代码有意义,必须使用 -w(等待)。没有它,即使测试失败,$? 也会是 0。参见 docs/CORPUS.md I.5。

模式

模式:错误 vs 正确——AndroidJUnit4 导入
// WRONG
import androidx.test.runner.AndroidJUnit4   // @Deprecated since androidx.test:runner 1.x
@RunWith(AndroidJUnit4::class)
class FooTest
// WRONG because: this class is @Deprecated at runner-1.7.0/.../AndroidJUnit4.java:43-44.
// The IDE flags it; codemods replace it; the doc explicitly redirects to ext.junit.
// RIGHT
import androidx.test.ext.junit.runners.AndroidJUnit4
@RunWith(AndroidJUnit4::class)
class FooTest
模式:错误 vs 正确——InstrumentationRegistry 导入
// WRONG
import androidx.test.InstrumentationRegistry             // @Deprecated, every member @InlineMe'd
val ctx = InstrumentationRegistry.getTargetContext()     // returns target package context, NOT the application
// WRONG because: monitor-1.8.0/androidx/test/InstrumentationRegistry.java:34 is @Deprecated.
// Use androidx.test.platform.app.InstrumentationRegistry + ApplicationProvider.
// RIGHT
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.core.app.ApplicationProvider
val instrumentation = InstrumentationRegistry.getInstrumentation()
val ctx: Context = ApplicationProvider.getApplicationContext()
模式:错误 vs 正确——Test Orchestrator 配置(最常见的配置错误)
// WRONG
dependencies {
    androidTestImplementation("androidx.test:orchestrator:1.6.1")
}
android.testOptions.execution = "ANDROIDX_TEST_ORCHESTRATOR"
// WRONG because: orchestrator must be installed as a separate APK on the device.
// `androidTestImplementation` puts it on the test APK classpath instead, where it does
// nothing. Tests still run in one shared process. No error, just silent inactivation.
// RIGHT
dependencies {
    androidTestUtil("androidx.test:orchestrator:1.6.1")
    androidTestUtil("androidx.test:services:1.6.0")  // required for clearPackageData
}
android.testOptions.execution = "ANDROIDX_TEST_ORCHESTRATOR"
模式:通过 RunnerArgs 在运行时筛选测试
# Run a single method
adb shell am instrument -w -r \
  -e class com.example.LoginActivityTest#loginSucceeds \
  com.example.app.test/androidx.test.runner.AndroidJUnitRunner

# Filter by size annotation
adb shell am instrument -w -r -e size small  com.example.app.test/androidx.test.runner.AndroidJUnitRunner

# Shard across N runners (CI fan-out)
adb shell am instrument -w -r -e numShards 4 -e shardIndex 0 \
  com.example.app.test/androidx.test.runner.AndroidJUnitRunner

受支持的 -e 键完整列表位于 runner-1.7.0/androidx/test/internal/runner/RunnerArgs.java:54-99(class、package、notClass、size、annotation、notAnnotation、numShards、shardIndex、clearPackageData、coverage、debug、listener 等)。若要在测试中读取这些参数,请使用 InstrumentationRegistry.getArguments()。

强制规则

  • 必须在 @RunWith(...) 中使用 androidx.test.ext.junit.runners.AndroidJUnit4。androidx.test.runner.AndroidJUnit4 形式已标记为 @Deprecated。
  • 必须使用 androidx.test.platform.app.InstrumentationRegistry。不带包名段 platform.app 的 androidx.test.InstrumentationRegistry 已标记为 @Deprecated。
  • 必须将 androidx.test:orchestrator 添加到 androidTestUtil,绝不能添加到 androidTestImplementation。放在错误的配置项中会静默失败。
  • 必须设置 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"(这里指 Runner 类——它未弃用,只有测试 Runner 注解已弃用)。
  • 必须为 am instrument 传入 -w,这样退出代码才能反映测试结果。不加此选项时,$? 没有意义。
  • 不得调用 Thread.sleep 来等待 instrumentation 生命周期(onCreate、onStart)。请使用 ActivityScenario.moveToState(参见 ../../scenarios/launching-activities-with-activityscenario/SKILL.md)或 IdlingResource。
  • 不得在测试中通过原始的 adb shell pm grant 授予权限——请使用 GrantPermissionRule.grant(...),这样权限授予的范围仅限于测试类,并且结束后能干净地撤销。
  • 不得使用 @Rule public ActivityTestRule——它已标记为 @Deprecated。请使用 @get:Rule val rule = ActivityScenarioRule(...)。
  • **建议:**为每个 instrumentation 测试类添加 @SmallTest / @MediumTest / @LargeTest 注解,以便 CI 按大小分片筛选。
  • **建议:**即使个别测试看起来不需要,也要保留 testOptions.animationsDisabled = true;波纹效果或转场出现间歇性故障时,会悄无声息地破坏其他测试。

验证

  • ./gradlew :<module>:connectedDebugAndroidTest 运行时不会出现 Test runner not found 或 INSTRUMENTATION_FAILED。
  • 没有源文件导入 androidx.test.runner.AndroidJUnit4 或 androidx.test.InstrumentationRegistry(旧路径)。
  • grep -r "androidTestImplementation.*orchestrator" build.gradle* 没有返回任何结果——orchestrator 应添加到 androidTestUtil。
  • adb shell am instrument -w -r -e size small <pkg>.test/androidx.test.runner.AndroidJUnitRunner 在测试失败时返回非零退出代码。
  • 如果启用了 orchestrator,adb logcat | grep AndroidTestOrchestrator 会显示每个测试的进程启动情况。
  • 模块的 android 块中设置了 testOptions.animationsDisabled = true。
  • 测试所需的任何运行时权限都有对应的 @get:Rule val rule = GrantPermissionRule.grant(...)(而不是手动运行 adb shell pm grant)。

参考资料

  • Android Developers — AndroidJUnitRunner 概览:https://developer.android.com/training/testing/instrumented-tests/androidx-test-libraries/runner
  • Android Developers — Test Orchestrator:https://developer.android.com/training/testing/instrumented-tests/androidx-test-libraries/runner#use-android
  • AndroidX Test 发布说明:https://developer.android.com/jetpack/androidx/releases/test
  • runner-1.7.0/androidx/test/runner/AndroidJUnit4.java 第 41-44 行 — @Deprecated public final class AndroidJUnit4 extends Runner。
  • junit-1.3.0/androidx/test/ext/junit/runners/AndroidJUnit4.java 第 49 行 — 当前未弃用的 Runner;在 JVM 上委托给 Robolectric,在设备上委托给 AndroidJUnit4ClassRunner。
  • runner-1.7.0/androidx/test/runner/AndroidJUnitRunner.java 第 270-372 行 — am instrument 调用的 Runner 类;生命周期和 orchestrator 等待逻辑。
  • runner-1.7.0/androidx/test/internal/runner/RunnerArgs.java 第 54-99 行 — Runner 接受的所有 -e 键的权威列表。
  • monitor-1.8.0/androidx/test/InstrumentationRegistry.java 第 34 行 — @Deprecated 的旧版注册表;每个成员都通过 @InlineMe 指向 platform.app 形式。
  • monitor-1.8.0/androidx/test/platform/app/InstrumentationRegistry.java — 当前权威注册表。
  • rules-1.7.0/androidx/test/rule/GrantPermissionRule.java 第 1-100 行 — static GrantPermissionRule grant(String...)。
  • runner-1.7.0/androidx/test/filters/SdkSuppress.java 第 39-58 行 — minSdkVersion/maxSdkVersion/excludedSdks/codeName。
  • tasks/research/R1-androidx-test-core.md — Runner / test-core 深入分析(Runner 部分见第 269-740 行;关于两个 AndroidJUnit4 引发的混淆见第 481-490 行)。
  • docs/CORPUS.md 第 H 节 — instrumentation 库坐标和弃用表。
  • 同类技能:../../managed-devices/running-tests-on-gradle-managed-devices/SKILL.md — 在 Gradle 配置、启动和关闭的模拟器上运行这些测试(可在 CI 中复现)。
  • 跨集合:../../../adb/tests/running-instrumented-tests-via-adb/SKILL.md — 通过 adb shell am instrument -w -r 直接调用 Runner。
  • 跨集合:../../../platform/legacy/migrating-from-android-test-classes/SKILL.md — 如果模块仍使用旧版 android.test.* 类 / InstrumentationTestRunner,请先将其迁移到此 Runner 技术栈。

评论

支持 Markdown