You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 23:15:03