JetPack Compose中Espresso测试报错:未找到匹配视图
运行Espresso原生测试时,每次都会抛出androidx.test.espresso.NoMatchingViewException,提示找不到匹配android.widget.TextView且文本为“Count”的视图。相关情况:
- string.xml中已定义“Count”字符串,App在模拟器运行正常
- 移除测试代码中的
.check(matches(isDisplayed()))后,测试可以通过 - 使用Compose专用的
createAndroidComposeRule编写测试时,能正常检测到视图并通过测试
MainActivity.kt代码
package com.example.testing import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material.Button import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.example.testing.ui.theme.TestingTheme class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { TestingTheme { // A surface container using the 'background' color from the theme Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background ) { Greeting() } } } } } @Composable fun Greeting() { var counter by remember{ mutableStateOf(0) } Column( modifier = Modifier .fillMaxSize() .wrapContentSize(Alignment.Center) ) { Text(text = stringResource(id = R.string.Count), modifier = Modifier .padding(8.dp) .testTag(stringResource(id = R.string.))) // 此处stringResource参数不完整,需修正为具体ID Button(onClick = { counter++ }) { Text(text = stringResource(id = R.string.Increment)) } } } @Preview(showBackground = true) @Composable fun DefaultPreview() { TestingTheme { Greeting() } }
测试代码(test.kt)
package com.example.testing import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import com.microsoft.appcenter.espresso.ReportHelper; import androidx.lifecycle.Lifecycle import androidx.test.core.app.ActivityScenario import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.* import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.LargeTest import com.microsoft.appcenter.espresso.Factory import org.junit.After import org.junit.Test import org.junit.runner.RunWith import org.junit.Assert.* import org.junit.Before import org.junit.Rule /** * Instrumented test, which will execute on an Android device. * * See [testing documentation](http://d.android.com/tools/testing). */ @RunWith(AndroidJUnit4::class) @LargeTest class ExampleInstrumentedTest { @Rule val activityRule = ActivityScenarioRule(MainActivity::class.java) //espresso @Test fun testClick() { onView(withText("Count")).check(matches(isDisplayed())) } }
根本原因
Espresso原生API是为传统Android View系统设计的,而JetPack Compose的UI元素并非基于原生View(比如Compose的Text组件不会生成android.widget.TextView实例),因此原生Espresso无法识别这些Compose元素。
解决方案
推荐:使用Compose专用测试API
这是官方推荐的Compose UI测试方案,你已经验证过createAndroidComposeRule可以正常工作,示例修正后的测试代码:@RunWith(AndroidJUnit4::class) @LargeTest class ExampleInstrumentedTest { @Rule val composeRule = createAndroidComposeRule<MainActivity>() @Test fun testClick() { composeRule.onNodeWithText(composeRule.activity.getString(R.string.Count)) .assertIsDisplayed() } }不推荐:强行适配原生Espresso
如果必须使用原生Espresso,需要将Compose元素包裹在AndroidView中,或者启用Compose的View兼容性层,但这会增加代码复杂度,且不符合Compose的设计理念,示例(仅作演示):@Composable fun Greeting() { var counter by remember{ mutableStateOf(0) } Column( modifier = Modifier .fillMaxSize() .wrapContentSize(Alignment.Center) ) { AndroidView(factory = { context -> TextView(context).apply { text = context.getString(R.string.Count) setPadding(8.dp.toPx().toInt(), 8.dp.toPx().toInt(), 8.dp.toPx().toInt(), 8.dp.toPx().toInt()) } }) Button(onClick = { counter++ }) { Text(text = stringResource(id = R.string.Increment)) } } }修正代码中的小错误
你在Text组件的testTag中使用了stringResource(id = R.string.),这是不完整的调用,需要定义一个专门的测试标签字符串(比如在string.xml中添加<string name="count_test_tag">count_tag</string>),然后改为.testTag(stringResource(id = R.string.count_test_tag)),这虽然不影响当前测试问题,但属于代码错误。
内容的提问来源于stack exchange,提问作者Deeshant Kotnala

