3. 测试环境初始化:创建AndroidJUnit4测试类、设置ActivityTestRule与ComposeTestRule
3.1 原理
在混合 Espresso + Compose 的测试项目中,测试环境初始化决定了组件能否正确绑定、生命周期是否同步。Android 测试框架通过 Rule 机制为每个测试方法提供可控的 Activity/Compose 运行环境:
- ActivityTestRule<T>:启动指定的 Activity,并保证
@Before/@After在生命周期内执行;Espresso 的onView()默认在此 Activity 上查找视图。 - ComposeTestRule(通常使用
createAndroidComposeRule<T>()):同时启动 Activity 并创建 Compose 测试宿主,允许混合调用 compose 的onNode()和 Espresso 的onView()。
两个 Rule 必须通过 @Rule 注解声明,且只能有一个 Activity 实例。如果混用,需保证 ComposeTestRule 排在 ActivityTestRule 之前或使用组合 Rule 避免冲突。
3.2 实现流程
- 创建测试类:继承
androidx.test.ext.junit.runners.AndroidJUnit4,使用@RunWith(AndroidJUnit4::class)。 - 声明 Rule:定义
@get:Rule属性,类型为ActivityTestRule<MainActivity>或createAndroidComposeRule<MainActivity>()。 - 初始化被测组件:在
@Before中通过 Rule 的activity属性获取 Activity 实例,或直接使用 Compose 测试语法。 - 编写测试方法:标注
@Test,混合使用 Espresso / Compose 断言。 - 清理(可选):在
@After中释放资源。
3.3 代码示例:完整的测试类骨架
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class MainActivityTest {
// 方案A:纯 Espresso(只使用ActivityTestRule)
@get:Rule
val activityRule = ActivityScenarioRule(MainActivity::class.java)
// 方案B:混合 Compose + Espresso(推荐)
@get:Rule
val composeRule = createAndroidComposeRule<MainActivity>()
@Test
fun testEspressoOnly() {
onView(withText("Hello"))
.check(matches(isDisplayed()))
}
@Test
fun testComposeOnly() {
composeRule.onNodeWithText("Hello")
.assertExists()
}
@Test
fun testMixed() {
// 先通过 Compose 点击
composeRule.onNodeWithText("Button").performClick()
// 再用 Espresso 验证 Toast
onView(withText("Clicked!")).check(matches(isDisplayed()))
}
}
提示:如果同时声明
ActivityScenarioRule 和 createAndroidComposeRule,请确保 Compose Rule 排在最前面,否则可能因 Activity 重复启动导致 IllegalStateException。
3.4 对比:ActivityTestRule vs ComposeTestRule
| 特性 | ActivityTestRule | ComposeTestRule (createAndroidComposeRule) |
|---|---|---|
| 主要用途 | 启动 Activity,支持 Espresso 纯 View 测试 | 启动 Activity + Compose 宿主,支持混合测试 |
| 依赖库 | androidx.test.espresso:espresso-core |
androidx.compose.ui:ui-test-junit4 |
| 提供的方法 | activity、scenario |
onNode()、onNodeWithText()、activity、setContent() |
| 生命周期控制 | 自动管理 @Before/@After |
自动管理,并额外提供 Compose 的 recomposition 等待 |
| 常见错误 | Activity 未启动时调用 activity 属性 |
忘记使用 createAndroidComposeRule 导致无 Compose 环境 |
3.5 常见错误与调试
- 错误1:
java.lang.IllegalStateException: No activity found—— 在@Before中过早调用activity属性。解决方案:将初始化逻辑移到@Test方法中,或使用ActivityScenario的onActivity回调。 - 错误2:同时使用两个 Rule 导致 Activity 启动两次。解决方案:只保留一个 Rule,或确保
ComposeTestRule是唯一的 Activity 启动入口。 - 调试技巧:在
@Test开头添加Thread.sleep(500)确保界面渲染完成(仅用于临时调试,不推荐用于生产)。
稳定性优化:在 CI 环境中,建议为
createAndroidComposeRule 设置 useUnmergedTree = false 以提升语义节点匹配速度。同时关闭动画(通过 @get:Rule @JvmField val animationRule = DisableAnimationsRule())。
3.6 测试环境初始化结构图
3.7 部署检查清单
- 确认 build.gradle 已添加依赖:
androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.compose.ui:ui-test-junit4:1.6.0' - 测试类必须位于
src/androidTest目录下。 - 确保
@Rule字段为public或使用@get:Rule修饰;不能使用@JvmField重复。 - 混合 Rule 时移除
ActivityTestRule,只保留ComposeTestRule。