UI 自动化的价值,在于为团队提供可靠的产品质量反馈。但界面一次普通改动,就可能让团队花不少时间维护定位器:按钮改名、属性变化或层级调整,都可能导致大量测试失败,即使业务行为本身并未改变。作者观察到,有些团队排查测试失败的时间,已经超过调查产品问题的时间。这种转变并不显眼,却会侵蚀团队对自动化的信任,也会改变自动化实际带来的价值。

Pranav Pandit 是一名资深质量工程师,在移动端和网页自动化领域有 15 年以上经验。他于 2026 年 8 月 4 日在 Ministry of Testing 发表了文章《当 AI 维护你的测试:设计安全的自愈自动化》。文章的核心设计选择很明确:定位器失败后,AI 可以作为受控备用方案,但不应成为主要测试执行引擎。 本文结合提供的学习笔记和原文整理。代码仅用于说明思路,并非作者实现的复现;额外的工程建议会单独标注。

定位器维护是自动化的隐性成本

Appium、Espresso 和 XCUITest 等框架依靠定位器查找并操作界面元素。定位器依赖界面结构和属性,文案或层级稍有变化,就可能触发“找不到元素”的失败。工程师查看日志、更新定位器,再重新执行测试。公共组件一次改动,就可能让几十个检查同时失败。

为什么不让 LLM 完成所有交互?确定性自动化更可预测,也更容易诊断。AI 有适应能力,但决策具有概率性,也可能很有把握地做错。更安全的混合方式是保留确定性的常规执行路径,只在特定且符合条件的定位器失败时调用 AI。

两条路径,各自负责不同的事

情况系统行为
原定位器匹配成功沿用常规确定性路径继续测试
符合条件的定位器查找失败检查缓存中是否有先前验证过的恢复结果
没有有效缓存解析意图和动作、识别界面上下文、精简界面层级,并请求一个候选结果或明确弃答
候选结果未通过验证保留原始失败,并记录拒绝候选的原因
候选结果通过验证执行动作、保留原有断言,再记录结果;条件合适时缓存恢复结果

业务断言失败、后端服务中断或应用崩溃,不应仅仅因为测试失败就被归类成定位器问题。找到替代定位器也不意味着测试已经通过:原有业务断言仍须继续执行。

恢复流程的关键环节

解析定位器及其用途

解析器把失败的定位器和它在测试中的用法结合起来,从而明确元素文案、类型、动作和流程提示。

CHECKOUT_CONTINUE_BUTTON = {
    "by": "xpath",
    "value": "//android.widget.Button[@text='Continue']"
}
click(CHECKOUT_CONTINUE_BUTTON)

parsed = {
    "text": "Continue",
    "class": "android.widget.Button",
    "action": "click",
    "context": "checkout",
    "element_type": "button"
}

这样可以把隐含指令转换成结构化信号。变量名 CHECKOUT_CONTINUE_BUTTON 有参考价值,但只是线索,不足以单独证明业务意图。

识别界面和流程上下文

弹窗和弹窗后面的页面可能各有一个名为“继续”的按钮。即便候选元素唯一、可见且可用,也可能是错误控件。有效上下文不应只有文案匹配,还应包括当前页面、业务流程阶段、最前端的弹窗,以及测试中前后相连的交互。

精简 XML 时保留有用结构

大型移动界面层级会增加模型输入和判断歧义。精简过程应保留相关候选项,同时保留足够的父子、兄弟和弹窗关系,供后续判断。

def prune_xml(nodes, signals):
    candidates = []
    for node in nodes:
        text = (node.text or "").lower()
        if signals["text"].lower() in text:
            if node.class_name in {"Button", "TextView"}:
                candidates.append(node)
    return candidates[:5]

这段简化示例使用精确文案匹配。如果“继续”合法地改成“下一步”,这种写法就找不到元素。真实实现需要识别经过确认的文案变化,同时保留选中正确控件所需的层级关系。

让模型在约束下提出候选结果

不要只要求模型“修好测试”。应提供结构化意图、动作、界面上下文和精简后的层级信息;尽量要求稳定的定位方式,并在证据不足时明确返回 EMPTY。

任务:为失败的测试步骤提出一个定位器。
输入:测试意图、交互动作、界面上下文、精简后的界面层级。
约束:
- 必须匹配预期交互和界面上下文。
- 优先使用稳定属性。
- 拒绝含糊或缺少证据的候选项。
- 证据不足时返回 EMPTY。
输出:只返回一个定位器,或 EMPTY。

允许模型弃答是一项安全机制。相比证据不足却强行恢复,明确表示无法判断更可靠。

将生成和验证分开

模型负责提出定位器,是否允许执行则由确定性检查决定。文章列出的检查包括:匹配结果唯一、元素类型正确、元素可见、交互状态符合要求。

def validate_candidate(driver, locator, action):
    elements = driver.find_elements(*locator)
    if len(elements) != 1:
        return False
    element = elements[0]
    if not element.is_displayed():
        return False
    if action == "click" and not element.is_enabled():
        return False
    return True

这个示例只展示了基础检查。is_enabled() 并不等于完整的可点击性验证。真实实现还需考虑遮挡、当前活动窗口、平台行为,以及验证和执行之间的状态变化。点击成功也不能证明预期业务结果已经发生。

缓存与日志是可靠性基础设施

如果同一次界面改动影响几十个测试,每个测试都重新调用 LLM 并不划算。可以缓存此前通过验证的恢复结果,但必须限定适用范围:应用版本、平台、页面、动作、原定位器和有效期都可能影响结果是否仍然可靠。

审计日志应能回答:为什么开始恢复?模型收到什么证据?验证器为什么接受或拒绝候选结果?原始测试的业务断言后来是否通过?缺少这些信息,自愈只会把显式失败变成不透明的决定。

三种采用方式

策略处理方式权衡
仅运行时恢复本次执行临时使用恢复定位器,不修改代码库代码变更风险最低,但仍要监控执行结果
人工评审生成代码修改或拉取请求,由工程师评审兼顾辅助、追溯性和现有代码评审
自动合并未经人工审批,自动写回并合并修复风险最高,不应作为默认方式

先从仅运行时恢复开始,积累可靠性证据。系统表现稳定后,再考虑自动准备拉取请求。合并代码与恢复一次测试执行,是不同级别的授权;前者需要更严格的证据、回归检查、回滚机制和权限控制。

四十多个测试失败的案例说明了什么

提供的学习笔记描述了这样一个案例:按钮文案从“继续”改为“下一步”,导致一次变更后有 40 多个测试失败。系统恢复了大部分测试,但仍有少数候选匹配到了其他流程中相似的控件。

恢复时要确认找到的元素确实属于当前测试的业务步骤。这个案例中,团队把重点放在补充上下文和约束上,没有把换用更强的模型当作唯一办法。

还有一种质量风险不能忽略:如果按钮改名本身违反了需求或可访问性约定,自愈不应悄悄把产品回归当成正常变化。能否接受恢复,要看测试究竟在保护什么。

需要明确保留的局限

原文讨论了四种持续存在的限制:

  1. 意图仍可能不明确。 相似控件可能被混淆。
  2. 界面关系可能丢失。 精简层级时,可能删掉作出正确判断所需的信息。
  3. 提示词较脆弱。 改进一个场景,可能导致另一个场景退化。
  4. 恢复会增加延迟和成本。 模型调用需要额外处理和基础设施开销。

因此,自愈率高不能单独代表成功。系统可能恢复了更多失败,同时也增加了错误恢复;后者才是更危险的结果。

工程延伸:如何证明恢复是安全的

以下建议是基于原文进一步提出的工程做法,不应视为作者已经实现的功能。

区分恢复状态。 分别记录未尝试、已拒绝、已恢复和已验证。已恢复表示交互执行过,已验证表示相关业务后置条件也通过。

检查业务后置条件。 在结账页面点击“下一步”后,应断言界面进入了预期的下一阶段。结构验证回答控件能否操作,业务断言回答操作是否正确。

建立回归评估集。 覆盖文案变化、重复控件、弹窗、层级顺序变化、过期缓存、跨页面误匹配和真实产品缺陷。对提示词做版本管理,并记录每次运行所用的模型和层级精简配置;其中任何一项变化后,都重新运行这些案例。这样才能追溯回归,而不是凭印象判断上一次是否成功。

证据不足时默认停止。 候选结果含糊时,保留原始失败和诊断信息。支付、删除、权限变更等高影响操作,应采用更严格的恢复政策。

保护数据。 界面层级和日志可能包含个人信息或业务敏感信息。发送给外部模型前应减少数据并脱敏,同时明确保留期限和访问权限。

试点时可以跟踪定位器类失败占比、候选接受率、恢复后业务断言通过率、错误恢复率、评审耗时、增加的延迟和单次恢复成本。错误恢复和掩盖真实缺陷的情况应作为优先级最高的安全指标。

后续值得继续探索的问题

原文提出了四个方向:按元素类型设计不同提示词;对提示词修改做回归测试;从验证交互扩展到验证业务结果;引入更丰富的界面和用户流程上下文。这些改进可以逐步落地,不必一开始就自动修改代码。

原文也留下了几个适合团队讨论的问题:

  • 你会信任 AI 更新测试套件中的定位器吗?
  • 在接受 AI 生成的修复前,需要多少验证才够?
  • 自动化和人工监督的边界应该在哪里?
  • 如果尝试过类似做法,哪些地方有效,哪些没有达到预期?

自愈的目标,是在不削弱测试发现真实产品问题能力的前提下,减少本可避免的维护工作。确定性自动化让执行可预测,AI 提出受约束的恢复候选,验证层决定能否继续,可追溯的证据让工程师保有控制权。

如果自动化修复了定位器,却悄悄点击了错误的控件,风险只会藏得更深。

参考资料

本文根据提供的详细学习笔记整理,并对照原文及其延伸阅读复核。代码仅作说明;工程延伸建议已单独标注。

分享