上周帮一个团队看 AI 生成的 UI 自动化脚本。
脚本跑得很漂亮:打开页面、填表、点击提交,最后页面弹出“提交成功”,报告里一片绿色。
但测试同学顺手去后台查了一下——这笔申请根本没有创建成功。
原因并不复杂:前端 Toast 提示先出来了,接口实际返回异常;而 AI 生成的脚本把“看见成功提示”当成了唯一断言。
这类问题在研发团队接入 Coding Agent、AI 自动化之后,会越来越常见。
AI 很会“把流程跑完”,但不天然知道:
页面完成一次点击,和业务真正成功,不是一回事。
01 AI 写出了脚本,为什么还会出现假通过?
现在用 Codex、Claude Code 这类工具补一条 Playwright 脚本,已经很方便。
给它一个页面、一段需求描述,通常几十秒就能生成这样的代码:
def test_apply_refund(page):
page.goto("https://test.example.com/refund/apply")
page.get_by_label("订单号").fill("A20260831001")
page.get_by_label("退款原因").select_option("重复下单")
page.get_by_role("button", name="提交申请").click()
expect(page.get_by_text("提交成功")).to_be_visible()
它的问题在于:这段代码只验证了页面说自己成功了。
但在真实业务中,至少还可能出现几种情况:
点击后前端乐观更新,接口其实返回 500;
接口返回成功,但退款单没有正确落库;
落库成功,但状态机流转错误,例如本应是 PENDING,却直接进入了 CLOSED;
测试环境里残留旧数据,页面展示的是上一轮的结果。
所以,AI 自动化最危险的不是“脚本不会写”。
而是:脚本把错误的结果,当成了正确的验证标准。
02 给 AI 一个“UI—接口—业务状态一致性”Skill
解决这件事,不是每次都重新提醒 AI:
“不要只断言页面提示,还要校验接口和数据。”
更好的做法,是把这条测试原则固化成一个项目级 Skill。
例如,在仓库里放一个 ui-api-consistency/SKILL.md:
name: ui-api-consistency
description: 用于涉及创建、提交、支付、审批等关键业务操作的 UI 自动化测试
测试规则
- 不允许只用 Toast、弹窗或按钮状态作为成功断言。
- 必须捕获关键请求,并校验 HTTP 状态码和响应体关键字段。
- 对创建类操作,必须通过业务查询接口验证最终状态。
- 断言应覆盖:请求参数、接口响应、业务实体状态。
- 测试失败时,输出 requestId、响应体和页面截图,便于定位。
它的价值不在于多写了一个 Markdown 文件。
而在于以后无论是 Codex、Claude Code,还是团队里其他人调用 Agent 补脚本,都能沿用同一套质量规则。
Prompt 是一次性对话。 Skill 是可复用、可审查、可跟随项目演进的测试经验。
03 Playwright 负责操作,MCP 负责让 Agent 看见真实系统
有了规则,还要让 Agent 真正拿到验证业务结果的能力。
这时可以把能力拆开:
能力
在测试中的作用
Playwright
操作页面、监听网络请求、获取页面状态
MCP
连接 Swagger、测试数据服务、缺陷平台、业务查询接口等工具
Skills
固化什么时候必须做多层校验、失败后输出什么信息
Coding Agent
理解任务并组合调用这些能力,生成或维护测试代码
下面把刚才那条“假通过”脚本,改成真正能校验退款申请状态的版本:
import pytest
from playwright.sync_api import expect
@pytest.mark.e2e
def test_apply_refund_should_create_pending_refund(page, api_client):
order_no = "A20260831001"
page.goto("https://test.example.com/refund/apply")
page.get_by_label("订单号").fill(order_no)
page.get_by_label("退款原因").select_option("重复下单")
# 1. 点击动作和关键接口请求必须绑定
with page.expect_response(
lambda response: "/api/refunds" in response.url
and response.request.method == "POST"
) as response_info:
page.get_by_role("button", name="提交申请").click()
response = response_info.value
# 2. 校验接口真正成功,而不是只看页面提示
assert response.status == 201
payload = response.json()
refund_id = payload["data"]["refundId"]
assert payload["data"]["status"] == "PENDING"
# 3. 通过业务查询接口确认最终状态
refund = api_client.get(f"/api/refunds/{refund_id}").json()["data"]
assert refund["orderNo"] == order_no
assert refund["status"] == "PENDING"
# 4. 页面反馈只作为体验层补充校验
expect(page.get_by_text("退款申请已提交")).to_be_visible()
这段代码的核心不是“多写了几个断言”。
而是把一次业务提交,拆成了三个层次:
页面层:用户是否完成操作;
接口层:服务是否真正成功处理请求;
业务层:最终数据和状态是否符合规则。
这才是 AI 自动化在关键链路上应该有的测试思维。
04 这类 Skill,恰恰是团队接入 AI 后最该先沉淀的
很多团队一上来就让 AI 做“自动生成测试用例”“自动写脚本”。
真正跑一段时间才发现,难的不是生成,而是控制生成结果的质量。
建议优先沉淀这几类测试 Skills:
关键链路一致性 Skill:UI、接口、数据库或业务状态的联合校验;
失败归因 Skill:自动收集请求响应、日志、截图和 Trace,辅助区分产品 Bug、环境问题、脚本问题;
测试数据 Skill:生成数据、清理数据、避免测试之间互相污染;
需求风险分析 Skill:读取 PRD、历史缺陷和接口文档,输出高风险测试点;
回归报告 Skill:按团队规范汇总通过率、失败原因、风险项和发布建议。
Skills 不是替代测试工程师判断。
它做的是把那些反复验证过的判断标准,先交给 AI 严格执行。
当团队把这些规则逐步沉淀下来,AI 才不会只是“跑得很快的脚本生成器”,而会开始成为真正可控的质量协作对象。
如果你现在正在接触 Codex、Claude Code、Skills、MCP 或 Playwright,不妨先问自己一个问题:
AI 帮我把测试跑完了,它验证的是页面表象,还是业务事实?
这两者之间,往往就是一条 Skill 的距离。
我们近期也会围绕 Agent、MCP、Skills、RAG 和 AI 自动化测试,持续拆解能够放进真实研发流程的测试场景。