Espresso测试无法识别自定义View中Canvas绘制的文本如何解决
问题根因
- Espresso 官方提供的
withText匹配器仅对原生TextView及其子类生效,这类控件自带公开的getText()方法供匹配器读取文本内容。你自定义的CustomTextView直接继承自View,通过Canvas绘制文本,没有对外暴露绘制的文本内容,Espresso 无法识别你绘制的文本,因此匹配失败。 - 额外存在拼写不一致问题:自定义 View 中绘制的文本是
Text from Custom view(view 首字母小写),测试用例中写的是Text from Custom View(View 首字母大写),大小写不匹配也会导致校验失败。
解决方案
以下三种方案按需选择即可,优先选择前两种侵入性低、稳定性高的方案:
方案1:使用contentDescription轻量适配
如果不需要动态修改自定义 View 的文本,最简单的方式是给自定义 View 配置contentDescription,直接用 Espresso 自带的withContentDescription匹配器校验。
修改自定义 View 代码:
class CustomTextView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0 ) : View(context, attrs, defStyleAttr, defStyleRes) { private val text = "Text from Custom view" init { // 把文本内容设置为内容描述 contentDescription = text } private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL_AND_STROKE textSize = 48f color = Color.BLUE strokeWidth = 3f } override fun onDraw(canvas: Canvas?) { canvas?.drawText(text, width / 2f, height / 2f, paint) } }
修改测试用例:
@Test fun testCustomTextView() { onView(withContentDescription("Text from Custom view")).check(matches(isDisplayed())) }
方案2:自定义Espresso匹配器(支持动态文本场景)
如果你的自定义 View 支持动态修改文本,推荐使用该方案:
第一步:修改自定义View,暴露文本属性
class CustomTextView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0 ) : View(context, attrs, defStyleAttr, defStyleRes) { // 公开文本属性,修改后自动触发重绘 var text: String = "Text from Custom view" set(value) { field = value invalidate() } private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.FILL_AND_STROKE textSize = 48f color = Color.BLUE strokeWidth = 3f } override fun onDraw(canvas: Canvas?) { canvas?.drawText(text, width / 2f, height / 2f, paint) } }
第二步:编写自定义匹配器
fun withCustomText(expected: String): Matcher<View> { return object : TypeSafeMatcher<View>() { override fun matchesSafely(item: View): Boolean { if (item !is CustomTextView) return false return item.text == expected } override fun describeTo(description: Description) { description.appendText("CustomTextView with text: $expected") } } }
第三步:修改测试用例
@Test fun testCustomTextView() { onView(withCustomText("Text from Custom view")).check(matches(isDisplayed())) }
方案3:截图对比校验(不推荐)
如果完全不想修改自定义 View 代码,可以用 Espresso 的截图匹配能力校验绘制内容,但该方案受设备分辨率、系统主题、渲染差异影响大,测试稳定性差,仅做备选。
内容的提问来源于stack exchange,提问作者Nishant Jalan
相关产品推荐
相关产品推荐

