Playwright MCP + CI/CD:让每个PR都自动跑一遍AI驱动的冒烟测试

简介: 本文介绍如何用Playwright MCP重构PR冒烟测试:告别手动点测,让AI Agent自动执行主流程、截图留证、记录网络请求,生成可审查证据链与脚本草稿;强调“Agent取证、QA判断”分工,解决不可追溯、易漏检痛点。含CI配置、GitHub Actions实践与三层断言设计。

上个月,一个测试负责人跟我说了一件事。他们团队每周合入几十个PR,每次合入前都要求“跑一遍冒烟”。但实际操作是:QA手动打开测试环境,登录,点一遍主流程,截图,然后在PR下面回一句“冒烟通过”。这个流程有两个问题:第一,它不可复查。 三个月后回头看,没人知道那次“冒烟通过”到底点了什么、看了什么。第二,它会漏。 人重复点同一套流程,到第三十个PR的时候,注意力早就散了。

Playwright MCP给了一个不同的解法:让Agent在PR触发时,自己打开页面、自己探索、自己截图留证,最后由QA审查哪些动作值得沉淀成脚本。不是“AI替你测完”,而是“AI把冒烟变成一条可复查的工作流”。

一、先搞清楚:PR冒烟到底要让Agent做什么?
很多人一上来就想让Agent“自己判断这个PR有没有问题”。这个目标太模糊,Agent做不到,做出来也没人敢信。

PR冒烟的场景,真正适合Agent做的是三件事:

第一,探索和取证。 Agent收到一个PR,打开预览环境,按预设的主流程走一遍——登录、进入目标页面、执行关键操作。每一步截图、记录网络请求、保存页面快照。失败的地方保留完整现场。

第二,产出可审查的证据链。 不是一句“通过/失败”,而是“我点了什么、看到了什么、网络层返回了什么”。QA审查的是证据,不是结论。

第三,生成脚本草稿。 探索过程中稳定的步骤,Agent可以产出Playwright脚本草稿。但草稿不自动合入,由QA判断哪些值得进入冒烟集。

Agent负责“跑和记”,QA负责“判断和沉淀”。 这个分工是整条链路能成立的前提。

二、CI环境里跑Playwright MCP,和本地有什么不同?
本地跑MCP,浏览器是带界面的,你能看到Agent在点什么。CI环境没有显示器,必须切换到无头模式,而且需要额外的沙箱参数。

配置上有三个必须处理的地方。

第一,加--headless。 Playwright MCP默认是带界面模式,在CI的Linux runner上会直接失败。启动参数里必须显式声明无头模式。

{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["@playwright/mcp@latest", "--headless", "--browser", "chromium"]
}
}
}
--headless也可以用环境变量PLAYWRIGHT_MCP_HEADLESS=true替代,两种方式等价。

第二,加--no-sandbox。 大多数CI容器以root用户运行,Chromium的沙箱机制会拒绝启动。Docker部署的官方示例里同样带了--no-sandbox参数。不加这个参数,你会看到一个很典型的错误:Failed to launch browser: No usable sandbox。

第三,固定版本,不要用@latest。 本地开发用@latest没问题,CI里必须锁版本。@latest在某次依赖更新后可能引入行为变化,导致昨天绿的冒烟今天莫名其妙红了。项目里放一个.mcp.json,版本写死,和代码一起提交。

{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["@playwright/mcp@0.0.54", "--headless", "--no-sandbox", "--browser", "chromium"]
}
}
}
三、PR触发的GitHub Actions工作流怎么写?
核心逻辑是:PR创建或更新时,启动一个job,job里启动MCP Server,然后让Agent连接MCP、执行冒烟任务、上传证据。

下面是workflow的骨架,关键步骤都有注释。

name: AISmokeonPR
on:
pull_request:
types:[opened,synchronize,reopened]

jobs:
ai-smoke:
runs-on:ubuntu-latest
timeout-minutes:15
steps:
-uses:actions/checkout@v4

  -uses:actions/setup-node@v4
    with:
      node-version:22

  -name:Installdependencies
    run:|
      npm ci
      npx playwright install chromium --with-deps

  -name:StartPlaywrightMCP(headless)
    run:|
      npx @playwright/mcp@0.0.54 \
        --headless --no-sandbox --browser chromium \
        --port 8931 --host 0.0.0.0 &
      npx wait-on http://localhost:8931/mcp --timeout 30000

  -name:RunAIsmokeagent
    env:
      BASE_URL:${
  {secrets.PR_PREVIEW_URL}}
      OPENAI_API_KEY:${
  {secrets.OPENAI_API_KEY}}
    run:nodescripts/run-smoke-agent.js

  -name:Uploadevidence
    if:always()
    uses:actions/upload-artifact@v4
    with:
      name:smoke-evidence
      path:evidence/
      retention-days:7

这里有几个设计决策值得单独说。

为什么用--port 8931而不是STDIO? STDIO模式适合本地客户端直接拉起Server,但在CI里,Agent脚本和MCP Server是两个独立进程。用HTTP transport把Server跑成长期服务,Agent通过http://localhost:8931/mcp连接,进程管理更清晰。

wait-on那一步不能省。 MCP Server启动需要时间,如果Agent在Server就绪之前就发起连接,会拿到ECONNREFUSED。wait-on等的是HTTP端点可访问,比固定sleep可靠。

if: always()上传证据。 冒烟失败的时候,截图和网络日志恰恰是最有价值的东西。如果只在成功时上传,失败现场就丢了。

四、Agent脚本里到底做什么?
scripts/run-smoke-agent.js不需要很复杂。它的职责是:连上MCP Server,按预设的冒烟目标执行,收集证据,退出时给出机器可读的结果。

核心逻辑是这样的:

const { Client } = require("@modelcontextprotocol/sdk/client/index.js");
const { StreamableHTTPClientTransport } = require("@modelcontextprotocol/sdk/client/streamableHttp.js");

asyncfunction main() {
const transport = new StreamableHTTPClientTransport(
new URL("http://localhost:8931/mcp")
);
const client = new Client({ name: "smoke-agent", version: "1.0" }, { capabilities: {} });
await client.connect(transport);

const baseUrl = process.env.BASE_URL;
const evidence = { steps: [], failed: false };

// Step 1: 导航
await client.callTool({ name: "browser_navigate", arguments: { url: ${baseUrl}/login } });

// Step 2: 快照 —— Agent先看页面,再决定操作
const snap = await client.callTool({ name: "browser_snapshot", arguments: {} });
evidence.steps.push({ phase: "snapshot_login", snapshot: snap.content });

// Step 3: 根据快照中的ref执行操作(示例:填登录表单)
// 实际项目中,ref从snapshot返回的结构化文本中解析
await client.callTool({
name: "browser_type",
arguments: { target: "e5", text: process.env.SMOKE_USER, submit: false }
});
await client.callTool({
name: "browser_type",
arguments: { target: "e6", text: process.env.SMOKE_PASS, submit: true }
});

// Step 4: 验证关键断言
const verify = await client.callTool({
name: "browser_verify_text_visible",
arguments: { text: "Dashboard" }
});

if (verify.isError) evidence.failed = true;

// Step 5: 落盘证据
require("fs").writeFileSync("evidence/result.json", JSON.stringify(evidence, null, 2));
await client.close();
process.exit(evidence.failed ? 1 : 0);
}

main().catch(err => {
console.error(err);
process.exit(1);
});
这段代码里最重要的设计是Step 2和Step 3之间的顺序:先browser_snapshot,再基于快照返回的ref执行操作。如果跳过快照、直接猜CSS选择器,脚本只能在当前页面结构下跑通,页面一改就废。

还有一个容易被忽略的细节:ref只在当前快照内有效。 每次导航或页面重新渲染后,必须重新browser_snapshot获取新的引用。Agent脚本里如果复用过期ref,会表现为“点错元素”或Element not found。

五、断言分三层,别让“工具成功”冒充“业务通过”
这是PR冒烟最危险的一层。

browser_verify_text_visible返回成功,只证明页面上的文字可见。它不证明接口返回了200,不证明订单真的创建了,不证明状态机流转正确。前端乐观更新、接口500但Toast提前弹出、页面显示了“成功”但数据库里什么都没有——这些“假通过”在手动冒烟里靠人的经验能兜住,在Agent脚本里必须用显式断言兜住。

PR冒烟的断言至少分三层:

第一层:工具层。 MCP工具调用没有返回isError。

第二层:页面层。 关键元素可见、关键文案出现。browser_verify_text_visible属于这一层。

第三层:业务层。 这是Agent脚本里最需要显式设计的一层。比如捕获关键网络请求并校验状态码,或者调用一个业务查询接口确认最终状态。如果冒烟目标涉及支付、订单、库存,第三层不能省。 没有第三层的冒烟,只是在验证“页面画出来了”,不是在验证“功能没坏”。

六、Agent跑出来的脚本草稿,要不要直接合入?
Jiten20的Agentic QA框架里有一个设计值得借鉴:Planner用MCP做浏览器探索,产出基于证据的测试计划;Generator只读取“经过QA审批的”测试场景,才生成Playwright代码;Healer分析失败,但修复方案需要人工批准才能合入。

这套流程的核心是人工审批门禁。Agent在PR里跑出来的冒烟结果和脚本草稿,不应该自动变成正式冒烟集的一部分。QA需要审查的是:这个步骤在业务上是不是关键路径?这个断言是不是太弱?这个选择器在未来UI改版时会不会碎?

Agent的价值是“把现场带回来”,不是“替你做决定”。

七、避坑指南
坑一:CI里忘了--headless。 带界面模式在无显示器的runner上直接失败,报错信息不一定直观。无头模式同时能省掉约200MB内存,对CI runner更友好。

坑二:用了@latest。 CI里版本必须锁死。@latest会在某次上游更新后让行为漂移,而且漂移的那一天,你正在排查一个和依赖更新无关的业务Bug。

坑三:跳过browser_snapshot直接操作。 Agent靠猜CSS选择器写的脚本,只能在当前构建上跑通。先observe再act,是Playwright MCP工作流的基础约束。

坑四:只断言页面文案。 页面说“成功”不等于业务成功。至少加一层网络请求校验,涉及资金和状态的冒烟必须加业务层断言。

坑五:让Agent自动合入脚本。 Agent生成的脚本草稿是“线索”,不是“资产”。草稿进正式冒烟集之前,必须有人判断它值不值得留下。

最后
PR冒烟这件事,过去的核心矛盾不是“跑得慢”,是“跑得不可复查” 。手动点一遍,截图存在某个人电脑里,结论写在PR评论里,三个月后什么都追溯不到。

Playwright MCP在CI/CD里的真正价值,不是让Agent“替人点按钮”,是把冒烟从一次性的手动动作,变成一条有快照、有截图、有网络日志、可回放、可审查的工作流。

Agent做机器的活:打开、操作、取证、落盘。人做人的活:判断哪些证据是有效的、哪些步骤值得沉淀、这个PR到底能不能合。

这条线画清楚了,PR冒烟才真正从“走过场”变成“有据可查”。

本文部分内容参考了霍格沃兹测试开发学社整理的相关技术资料,主要涉及软件测试、自动化测试、测试开发及 AI 测试等内容,侧重测试实践、工具应用与工程经验整理。

相关文章
|
7天前
|
云安全 人工智能 运维
当攻击全面Agent化,你的安全防御能否跟上AI Speed?
多起客户侧AK泄露事件应急的真实观察
1311 6
|
5天前
|
人工智能 API 开发工具
DeepSeek Harness 神插件:白嫖 13 家大厂模型
`dsh-our-free-model` 是 DeepSeek Harness 神级插件:零配置、零API Key、零账号,聚合13家大厂免费模型(含DeepSeek V4.1 Flash、Kimi K3等),支持本地网关、用量看板与自动降级,真正实现“白嫖即用”。
|
10天前
|
存储 人工智能 安全
千问办公官网入口网页版登录:qwenwork.cn 注册送2000积分、登录送100积分
阿里云千问办公(QwenWork)是AI智能工作平台,支持一句话生成PPT、表格、网页、视频及数据分析。个人免费版送2000积分+每日登录赠100分,含1GB存储、5个发布页;企业版198元/席/月起。阿里千问办公QwenWork官网:https://t.aliyun.com/U/0VCTGt 阿里AI工作平台,一句话完成数据分析、PPT 生成、视频剪辑、网页搭建等复杂任务
|
1月前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
9965 33
|
5天前
|
人工智能 开发者
Qoder CN 大使计划正式开启|无限创造,等你一起
全年免费积分、优先内测、活动支持与更多共创机会,邀请用 Qoder 把想法变成现实的你。
523 6
|
16天前
|
存储 人工智能 并行计算
大模型本地部署终端选型方法论:以 Qwen3.8-27B 为例的四档分层完整流程
本文提出一套大模型本地部署终端选型方法论:定约束、定档位、定框架、定参数四步决策法,配合入门、主力、质量、无损四档分层模型。以 Qwen3.8-27B 实测数据为例,逐环节解读显存、带宽、存储、散热、系统、预算等要素,给出面向不同预算的优选方案、决策自查清单与市场观察框架。文末前瞻 AI 笔记本的 CPU+GPU 与统一内存两条路线,论证四步决策法在新品类上的延续性。
|
7天前
|
Web App开发 人工智能 JavaScript
太猛了,一个月干到15W+,给大家推荐一个最近爆火的开源逆向工具:REA
REA(Reverse Engineer Anything)是近期爆火的开源AI逆向工具,4月发布,1个月内斩获15万+ Star。它让AI Agent无需源码即可分析二进制、APK、JS/Electron、HAR包等17类目标,自动反编译、生成可运行代码,并强制返回带证据与局限的结论。全本地执行,MIT协议,支持Hopper/Ghidra/IDA等引擎,一行命令即可上手。
747 2
太猛了,一个月干到15W+,给大家推荐一个最近爆火的开源逆向工具:REA
|
15天前
|
人工智能 JSON 自然语言处理
2026 年 Jev 决策模型深度拆解:原理解读、实战测评与保姆级落地教程
有一款特殊AI模型在开发者圈子刷屏,它摒弃传统大模型擅长的对话聊天能力,专注做高速结构化决策,它就是TypeSafe AI推出的Jev模型。该模型由ChatGPT共同发明人Diogo Almeida主导研发,定位为**System One Model(系统一模型)**,对标人类大脑快速直觉判断的思维模式,在响应延迟、调用成本、结构化输出稳定性上相比传统生成式大模型有着巨大差异。本文会完整拆解Jev底层原理、三大核心原语能力、适用业务场景,同时提供可直接运行的curl、Python代码示例,并且结合多组实测数据,客观分析模型优势与能力边界,帮助普通开发者和AI应用从业者快速上手落地。
1069 1