Jetpack Compose测试报错:如何断言按钮应用MaterialTheme.colors.primary?
解决Compose测试中“@Composable invocations can only happen from @Composables”错误
错误原因
测试代码不属于@Composable上下文,直接调用MaterialTheme.colors.primary会触发该错误——MaterialTheme的颜色属性仅能在Composable函数内部访问。
正确解决方案
方案1:在Composable上下文内获取颜色并执行断言
将颜色获取逻辑放在setContent块中(测试的Composable上下文),再传递给断言函数:
composeTestRule.setContent { // 在Composable上下文内获取主题颜色 val primaryColor = MaterialTheme.colors.primary // 渲染测试目标组件 Btn(shopState = 1) // 执行断言 composeTestRule.onNodeWithText("Calculate").assertButtonColor(primaryColor) }
方案2:优化断言函数的颜色校验逻辑
原断言仅比较颜色空间名称,无法准确验证颜色值。修改断言函数,直接校验像素颜色:
fun SemanticsNodeInteraction.assertButtonColor(expectedColor: Color) { val capturedImage = captureToImage() // 取按钮文本区域的中心像素(可根据实际布局调整取点逻辑) val centerX = capturedImage.width / 2 val centerY = capturedImage.height / 2 val pixelValue = capturedImage.getPixel(centerX, centerY) val actualColor = Color(pixelValue) // 考虑浮点精度误差,使用delta值比较 assertEquals(expectedColor.red, actualColor.red, 0.01f) assertEquals(expectedColor.green, actualColor.green, 0.01f) assertEquals(expectedColor.blue, actualColor.blue, 0.01f) assertEquals(expectedColor.alpha, actualColor.alpha, 0.01f) }
方案3:通过语义属性传递颜色(高效无截图)
给组件添加自定义语义属性存储颜色,测试时直接读取语义属性,无需截图:
- 修改Btn组件,添加语义属性:
@Composable fun Btn(shopState: Int) { var color = MaterialTheme.colors.primary if (shopState == 2) { color = MaterialTheme.colors.error } else if (shopState == 3) { color = MaterialTheme.colors.secondary } Button(onClick = {}) { Text( text = "Calculate", color = color, modifier = Modifier.semantics { // 存储文本颜色到语义属性 this["buttonTextColor"] = color } ) } }
- 编写对应的断言函数:
fun SemanticsNodeInteraction.assertButtonColor(expectedColor: Color) { val semanticsNode = fetchSemanticsNode() val actualColor = semanticsNode.config.getOrElse("buttonTextColor") { fail("未找到按钮文本颜色的语义属性") } as Color assertEquals(expectedColor, actualColor) }
内容的提问来源于stack exchange,提问作者Hobo
相关产品推荐
相关产品推荐

