Android Espresso无法找到自定义View内的EditText,如何无需自定义ViewAction使用typeText
解决方案
你可以通过自定义View Matcher的方式直接定位到自定义View内部的EditText,完全支持Espresso所有原生操作包括typeText,无需修改现有业务代码:
步骤1:定义自定义Matcher
fun withInnerEditTextInFormView(parentFormViewId: Int): Matcher<View> { return object : TypeSafeMatcher<View>() { override fun matchesSafely(view: View): Boolean { // 先匹配目标EditText的id if (view.id != R.id.etForm) return false // 递归遍历所有父节点,判断是否属于对应id的自定义FormView var currentParent = view.parent while (currentParent is View) { if (currentParent.id == parentFormViewId) { return true } currentParent = currentParent.parent } return false } override fun describeTo(description: Description) { description.appendText("EditText R.id.etForm inside FormView with id $parentFormViewId") } } }
步骤2:测试中直接使用
@RunWith(AndroidJUnit4::class) class LoginFragmentTest : BaseTest() { @Test @Throws(InterruptedException::class) fun testLoginForm() { val navController = TestNavHostController(ApplicationProvider.getApplicationContext()) val loginScenario = launchFragmentInContainer<LoginFragment>() loginScenario.onFragment { fragment -> navController.setGraph(R.navigation.nav_login) Navigation.setViewNavController(fragment.requireView(), navController) } // 先点击获取焦点再输入,避免typeText失效 onView(withInnerEditTextInFormView(R.id.email)).perform(click(), typeText("user@email.com")) onView(withInnerEditTextInFormView(R.id.password)).perform(click(), typeText("123456")) onView(withId(R.id.login)).perform(click()) } }
原报错原因说明
你之前使用isDescendantOfA匹配失败,通常是因为自定义FormView在Inflate布局时多加了一层中间容器,导致etForm的直接祖先节点中没有对应id的FormView。自定义Matcher会递归遍历所有层级的父节点,不会受中间布局层级的影响。
可选补充方案
如果可以修改业务代码,你也可以在FormView初始化时给内部的etForm设置contentDescription,测试时直接通过withContentDescription匹配,性能和适配性同样优秀。
内容的提问来源于stack exchange,提问作者Nouman Bhatti
相关产品推荐
相关产品推荐

