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 多个测试失败。系统恢复了大部分测试,但仍有少数候选匹配到了其他流程中相似的控件。
恢复时要确认找到的元素确实属于当前测试的业务步骤。这个案例中,团队把重点放在补充上下文和约束上,没有把换用更强的模型当作唯一办法。
还有一种质量风险不能忽略:如果按钮改名本身违反了需求或可访问性约定,自愈不应悄悄把产品回归当成正常变化。能否接受恢复,要看测试究竟在保护什么。
需要明确保留的局限
原文讨论了四种持续存在的限制:
- 意图仍可能不明确。 相似控件可能被混淆。
- 界面关系可能丢失。 精简层级时,可能删掉作出正确判断所需的信息。
- 提示词较脆弱。 改进一个场景,可能导致另一个场景退化。
- 恢复会增加延迟和成本。 模型调用需要额外处理和基础设施开销。
因此,自愈率高不能单独代表成功。系统可能恢复了更多失败,同时也增加了错误恢复;后者才是更危险的结果。
工程延伸:如何证明恢复是安全的
以下建议是基于原文进一步提出的工程做法,不应视为作者已经实现的功能。
区分恢复状态。 分别记录未尝试、已拒绝、已恢复和已验证。已恢复表示交互执行过,已验证表示相关业务后置条件也通过。
检查业务后置条件。 在结账页面点击“下一步”后,应断言界面进入了预期的下一阶段。结构验证回答控件能否操作,业务断言回答操作是否正确。
建立回归评估集。 覆盖文案变化、重复控件、弹窗、层级顺序变化、过期缓存、跨页面误匹配和真实产品缺陷。对提示词做版本管理,并记录每次运行所用的模型和层级精简配置;其中任何一项变化后,都重新运行这些案例。这样才能追溯回归,而不是凭印象判断上一次是否成功。
证据不足时默认停止。 候选结果含糊时,保留原始失败和诊断信息。支付、删除、权限变更等高影响操作,应采用更严格的恢复政策。
保护数据。 界面层级和日志可能包含个人信息或业务敏感信息。发送给外部模型前应减少数据并脱敏,同时明确保留期限和访问权限。
试点时可以跟踪定位器类失败占比、候选接受率、恢复后业务断言通过率、错误恢复率、评审耗时、增加的延迟和单次恢复成本。错误恢复和掩盖真实缺陷的情况应作为优先级最高的安全指标。
后续值得继续探索的问题
原文提出了四个方向:按元素类型设计不同提示词;对提示词修改做回归测试;从验证交互扩展到验证业务结果;引入更丰富的界面和用户流程上下文。这些改进可以逐步落地,不必一开始就自动修改代码。
原文也留下了几个适合团队讨论的问题:
- 你会信任 AI 更新测试套件中的定位器吗?
- 在接受 AI 生成的修复前,需要多少验证才够?
- 自动化和人工监督的边界应该在哪里?
- 如果尝试过类似做法,哪些地方有效,哪些没有达到预期?
自愈的目标,是在不削弱测试发现真实产品问题能力的前提下,减少本可避免的维护工作。确定性自动化让执行可预测,AI 提出受约束的恢复候选,验证层决定能否继续,可追溯的证据让工程师保有控制权。
如果自动化修复了定位器,却悄悄点击了错误的控件,风险只会藏得更深。
参考资料
- Pranav Pandit,《当 AI 维护你的测试:设计安全的自愈自动化》:https://www.ministryoftesting.com/insights/when-ai-maintains-your-tests-designing-safe-self-healing-automation
- Appium 文档:https://appium.io/docs/en/
- OpenAI 提示词工程指南:https://developers.openai.com/api/docs/guides/prompt-engineering
- O’Reilly,《提示词工程就是需求工程》:https://www.oreilly.com/radar/prompt-engineering-is-requirements-engineering/
- Ministry of Testing,《回到基础:重新思考如何在测试中使用 AI》:https://www.ministryoftesting.com/insights/back-to-the-basics-rethinking-how-we-use-ai-in-testing
本文根据提供的详细学习笔记整理,并对照原文及其延伸阅读复核。代码仅作说明;工程延伸建议已单独标注。