使用最新版本的 Compose 1.2.0 运行 Robolectric 单元测试时,使用
createAndroidComposeRule
的测试会失败并出现以下错误:
MyActivity 已设置内容。如果您已使用 ComposeView 填充 Activity,请确保在该 ComposeView 上调用 setContent,而不是在测试规则上调用;并确保在 ComposeTestRule 运行后完成对
的调用setContent {}
来自失败测试之一的代码:
composeTestRule.setContent {
Column {
Text(textTitle)
DemoScopedInjectedViewModelComposable()
}
}
仔细观察,错误消息很有帮助,即使它谈论的是
ComposeView
而不是 Activity
。但根据它:
composeTestRule.setContent { ... }
应改为:
composeTestRule.activity.setContent { ... }
并且测试应该运行而不会再出现此错误。
或者,确保您的活动正在扩展
ComponentActivity
,因此 composeTestRule.setContent { ... }
应该可以正常工作。
Compose 1.2 具有 forbidden 来覆盖活动规则的内容。但这仍然可以通过直接在 Activity 上设置内容来完成,而不是 Activity 测试规则。
解决此问题的最简单方法是不在活动规则上设置内容,而是在活动本身上设置内容。可以这样做:
composeTestRule.activity.runOnUiThread {
composeTestRule.activity.setContent {
Column {
Text(textTitle)
}
}
}
为了简化使用,您可以使用以下扩展
fun <R : TestRule, A : ComponentActivity> AndroidComposeTestRule<R, A>.setContentOnActivity(
content: @Composable () -> Unit
) {
this.activity.runOnUiThread {
this.activity.setContent {
content()
}
}
}
解决方案是从 Activity 中获取 Compose View(该 Activity 在测试规则中可用),然后在该 View 上调用
setContent
,而不是直接在测试规则上调用,如错误消息所示。
这是我创建的一个测试辅助函数,以避免在测试中出现此问题:
fun AndroidComposeTestRule<ActivityScenarioRule<MyActivity>, MyActivity>.clearAndSetContent(content: @Composable () -> Unit) {
(this.activity.findViewById<ViewGroup>(android.R.id.content)?.getChildAt(0) as? ComposeView)?.setContent(content)
?: this.setContent(content)
}
更新测试:
composeTestRule.clearAndSetContent {
Column {
Text(textTitle)
DemoScopedInjectedViewModelComposable()
}
}
作为参考,这是我的测试规则:
@get:Rule
val composeTestRule = createAndroidComposeRule<MyActivity>()
在设置
ComponentActivity
时使用MainActivity
代替composeTestRule
对我有用:
class MainActivityInstrumentedTest {
// @get:Rule
// val composeTestRule = createAndroidComposeRule<MainActivity>()
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
}