
1. 为什么我用 AI 智能体来写浏览器自动化测试过去这几年我一直用 Selenium 和 Playwright 写端到端测试这类工具解决的问题很明确确定性的点击、输入、断言。但它们有一个绕不过去的痛点——脆弱。页面结构一改选择器就崩前端重构一次几十条用例跟着报废。每次回归测试跑挂一半以上都是定位器失效真正测出业务缺陷的反而是少数。维护成本高到让人怀疑自动化到底是在节省时间还是制造负担。Nova Act 这类 AI 驱动的浏览器智能体把问题换了个拆法不再要求你精确告诉浏览器“点击哪个XPath”而是直接描述最终目标比如“找到页面右上角的用户名输入框填入test_user然后点击登录按钮”。智能体自己去理解页面语义、规划操作路径、执行动作。上述能力对端到端测试来说意味着什么意味着测试不再绑定具体DOM结构而是绑定业务意图页面怎么改不关键关键是用户能不能完成这件事。这篇实战分享就用一个模拟项目 X一个典型的电商后台系统来演示怎么在 pytest 里集成 Nova Act、怎么写 AI 驱动的端到端测试、怎么用 OpenClaw 做运行时调度让整套流程稳定落地。内容适合已经写过一段时间 UI 自动化、想换思路的测试开发也适合刚接触浏览器智能体、想直接上手做事的读者。会包含完整代码、参数调优经验以及我踩过的坑。先说结论AI 驱动的浏览器自动化解决的不是“不用写代码”而是“不用写死每一步”。代码还是要写但主要写在这个层面——定义目标、校验结果、组织执行。2. Nova Act 的核心原理与整体设计思路2.1 它和 Playwright、Selenium 本质差在哪里传统自动化工具本质是一种“坐标执行器”你把每一步都告诉它它机械照做。就像给一个完全不懂业务的新员工下发精确到“把手指放到哪、按哪里”的指令。这个模式的优点是可控缺点是一旦场景描述与实际情况有出入整个流程就卡死。Nova Act 的模型架构本质上是“意图理解 多步规划 视觉定位”的组合。它接收的不是单步指令而是自然语言描述的任务目标比如“刷新订单列表把第一条订单的详情页打开提取订单编号”。模型会调用内部的视觉模型去观察当前浏览器截图决定先做什么、再做什么遇到弹窗时会自己关掉而不是报错。这种方式相当于把一个有经验的测试员放在浏览器前面——他知道怎么完成任务但不会拘泥于某一条路径。从工程角度说它最大的贡献是把“脚本可读性”和“业务可读性”统一了。Playwright 的脚本本质是操作序列的调用堆砌看代码很难知道这条测试的业务价值是什么Nova Act 的测试则像一篇文章描述清楚目标执行细节由模型编排。2.2 任务、动作、观察Nova Act 的三个核心抽象Nova Act 的编程模型里有三个核心概念需要先搞清楚任务Task一个完整的目标描述可以是一句话也可以拆成多个步骤。框架会在独立的浏览器会话里执行执行过程包含截图、状态推理、动作决策的循环。动作Action执行层支持的基础操作集包括点击、输入、滚动、等待、提取文本、按快捷键等。模型在内部自主决定调用哪些动作测试代码层面不直接控制但可以通过参数做限制。观察Observation模型决策依赖的上下文主要是浏览器当前页面的截图、DOM 相关信息、Console 日志等。这些观察数据会实时反馈给模型决定下一步动作。这三个抽象让调用方式化繁为简。测试代码里你不需要写一长串 click 和 send_keys只需要描述任务然后接收执行结果。我在模拟项目 X 里的第一个体验是一个 20 步的完整下单流程传统 Playwright 脚本大约需要 250 行代码换成 Nova Act 任务描述代码量降到 40 行左右而执行成功率经过参数调优后维持在 92% 以上。2.3 为什么优先选 AI 驱动而不是混合模式有人会问为什么不用 Playwright 处理稳定部分、AI 只处理容易变的环节混合模式也就是现在很流行的“AI 辅助定位 传统执行流”。我试过确实可行但引入了两套心智模型和两套调试链路。Nova Act 这种纯智能体模式的好处是执行路径由模型统一规划遇到异常时它自己会动态调整混合模式则容易卡在“AI 说点这里Playwright 过去后发现是个下拉框”这种结合部的隐性问题。如果你想降低维护成本优先考虑纯智能体模式如果你对延迟要求极高且页面极其稳定混合模式仍然有它的价值。3. pytest 集成的工程化拆解3.1 测试骨架fixture 怎么设计才顺手pytest 集成第一步是把 Nova Act 的会话生命周期管好。我的做法是在 conftest.py 里定义两个 fixture一个负责创建任务执行器一个负责清理浏览器会话。# conftest.py import pytest from nova_act import NovaAct pytest.fixture(scopefunction) def browser_agent(): 每个测试函数独立浏览器会话避免状态污染 agent NovaAct.create_session( headlessTrue, viewport{width: 1440, height: 900} ) yield agent agent.cleanup()这里有一个关键选择scope 用 function 而不是 session。曾有人问为什么每个用例都要重新开浏览器因为 AI 执行的任务是非确定性的如果多个用例共享一个会话前一个用例的页面状态可能会干扰模型的规划导致 flaky。牺牲一点时间换稳定性值得。3.2 让 AI 返回结构化结果而不是“执行完就结束”真实业务里你不仅要让智能体“做完事情”还要得到可断言的结果。这是用 AI 做测试的核心诉求。Nova Act 的任务执行接口支持在任务里附带提取要求比如def test_order_detail_extract(browser_agent): response browser_agent.run( task( 打开订单列表页找到金额最大的订单点击详情 提取订单编号、订单金额、订单状态三个字段 ), result_schema{ order_id: string, amount: float, status: string } ) assert response.data[order_id], 订单编号不应为空 assert response.data[amount] 100 assert response.data[status] in [已支付, 待发货, 已完成]result_schema 参数是这类框架最实用的设计之一模型在操作完成后会把页面上的非结构化内容整理成结构化 JSON 返回天然匹配 pytest 的断言体系。这样一来AI 测试不再是“帮你在页面上走了个流程”而是真正可以参与 CI 门禁的自动化用例。3.3 断言策略不要追求精确匹配写呼声测试断言时对 AI 返回的数据要有所取舍。精确匹配容易崩。比如提取订单金额模型可能返回“1,299.00”而不是 1299.0状态可能返回“order status: paid”而不是“已支付”。如果你在断言里写死精确值就会频繁误报。我的策略是分层处理关键业务规则用正则或归一化函数清洗后断言属性值用集合包含关系断言。核心是把断言焦点放在业务语义上比如“金额超过阈值”“状态在合法枚举内”而不是和页面文本逐字吻合。测试如果太挑剔反而体现不出 AI 自动化的抗变化优势。3.4 测试数据构造与隔离端到端测试最怕乱用线上数据。模拟项目 X 的做法是准备一套独立的测试环境并通过接口提前构造测试用户和订单数据。Nova Act 只负责操作页面数据准备用普通 reqHTTP 脚本搞定。这就涉及 pytest fixture 间的依赖协作pytest.fixture(scopeclass) def test_orders(): 提前在环境里造出三张不同状态的订单 setup_orders([pending, paid, refunded]) yield cleanup_orders()数据隔离做得好用例运行成功率直接就上一个台阶。数据混乱时AI 在页面上看到奇怪的内容它的规划会不准确有时甚至会把无关数据当目标。别让它处理脏数据那是测试平台该解决的问题。3.5 完整 pytest 运行配置pytest 配置建议用 ini 文件管理。我的配置里会加这几项[pytest] addopts -n 4 --dist loadgroup --maxfail5 markers regression: 回归测试 smoke: 冒烟测试 filterwarnings ignore-n 4 开启 xdist 并行四个浏览器会话同时跑整体运行时间可以缩短到原来的三分之一。不过要注意Nova Act 调用是 CPU 密集型的并行数建议和物理核数匹配不要盲目开启。实测在 8 核机器上开 4 个并行度表现最好开到 6 个之后不稳定率明显上升。4. OpenClaw 场景落地实战4.1 OpenClaw 在这套体系里扮演什么角色OpenClaw 是一个轻量级的浏览器自动化运行时框架负责管理智能体的执行环境、重试策略、执行日志和资源释放。你可以把它理解为“智能体调度壳”Nova Act 负责思考做什么OpenClaw 负责保证它能安稳定在跑起来、出错时知道怎么恢复。在模拟项目 X 里OpenClaw 承担了这样几件事任务队列把多条测试按依赖关系排入队列避免并发争抢资源。会话池维护一组预热的浏览器会话缩短每个用例的启动时间。失败重试策略定义环境级重试和断言级重试的不同规则这个细节后面展开。产物收集每个任务跑完自动保存截图、模型决策轨迹、控制台日志。不少人以为 OpenClaw 是个必须单独装集群的东西其实它的形态可以很轻。在你的 pytest 会话里直接以上下文管理器方式使用代码并不复杂from openclaw import Runtime def test_complex_checkout_flow(browser_agent): with Runtime.start(profileecom_backend) as runtime: task runtime.delegate( agentbrowser_agent, goal( 登录后台进入订单模块筛选待发货状态 对第一笔订单执行发货操作并确认状态变更 ), retry2, collect[screenshot, trace] ) result task.run() assert result.status success assert result.data[order_state] shipped4.2 场景一登录流程的冒烟测试这是最典型的一个场景。以前写登录测试要考虑验证码、弹窗、密码框的各种位子。现在直接描述任务def test_login_smoke(browser_agent, test_user): resp browser_agent.run( task( 打开后台登录页使用测试账号登录 登录成功后顶栏应该显示用户昵称 如果出现验证码弹窗尝试自行识别并填写 识别失败则点击获取人工帮助并停止 ), result_schema{logged_in: bool, nickname: string} ) assert resp.data[logged_in] is True注意任务描述里“如果出现验证码弹窗”这种条件语句这正是 AI 智能体的优势场景。传统脚本遇到验证码基本直接失败而模型会尝试各种手段确认专业度。我实测在小规模验证码出现率 30% 的环境里成功率仍然有 85%。4.3 场景二订单发货全流程回归回归测试的核心诉求是验证关键路径不出现退化。把人工可读的业务流程直接从测试文档贴进任务比在代码里翻译成操作序列直观得多def test_order_shipping_full_path(browser_agent, test_orders): resp browser_agent.run( task( 进入订单列表筛选出三笔待发货订单 对第一笔点击发货填写物流单号为 SF1234567890确认 回到列表页刷新筛选条件 确认该笔订单状态变为已发货 提取所有已发货订单的数量 ), result_schema{order_count: int, shipped_id: string} ) assert resp.data[shipped_id] 待发货订单的编号 # 从fixture传入 assert resp.data[order_count] 1这类流程最大的坑是物流单号需要手工生成。我直接把单号生成逻辑提前放 fixture 里然后传进任务描述避免每次随机生成导致的状态混乱。另一个心得是任务里要写明“回到列表页并刷新筛选条件”否则模型可能只停留在详情页导致后续判断失真。4.4 场景三跨页面数据一致性校验端到端测试里和调试最相关的场景是验证两个页面展示的数据是否一致。比如后台列表页显示的订单总额和财务报表页显示的订单总额应该对得上。传统自动化脚本需要分别取数值再对比AI 方式可以直接整合进一个任务def test_cross_page_amount_consistency(browser_agent): resp browser_agent.run( task( 先在订单列表页底部的统计栏读取订单总额 然后进入财务报表页读取同一天的销售总额 对比两个数字是否一致如果两者相差超过0.01 则把差额和两个页面的数值一起返回 ), result_schema{ list_total: float, report_total: float, difference: float } ) assert abs(resp.data[difference]) 0.01这种测试放在传统实现里会相当冗长而且页面布局一变就要重写。用 AI 实现后唯一需要维护的就是业务描述。最惊喜的是它遇到金额单位不同比如万元和元时能自己识别并换算这对脚本自动化来说是很难做的细节处理。4.5 定时巡检与告警AI 测试的另一个应用面除了 as 传统 CI 触发OpenClaw 还可以把这类测试改造成线上巡检的探针。我做了个尝试把同样的任务脚本挂到调度器每分钟跑一次只检查关键页面可用性和核心数据一致性失败时把截图和模型日志推送到群机器人。这其实是简单的“数字员工”雏形不用开发专门的巡检平台只需把流程描述清楚。巡检的价值和新建用例相比毫不逊色尤其是对大型系统——测试覆盖的用例再多生产环境出问题往往不会出在已被覆盖的路径上而巡检相当于持续保持有人在做业务级冒烟。5. Flaky 测试排查与调参实录5.1 偶发失败到底是谁背锅环境还是模型我用这套方案跑了三周发现 92% 的执行成功率背后值得调的细节比想象中的多。典型的失败分三类环境因素网络抖动导致加载超时。页面渲染速度不可控AI 规划迟疑会增加整体耗时长。模型理解偏差任务描述里有歧义模型执行了一个合理但不符合预期的路径。页面弹窗干扰包括浏览器通知授权、遮罩层、新版本条幅等模型虽然能应对大部分但偶尔会失手。排查思路很重要不要一上来就怀疑模型不行先看重试结果和日志。OpenClaw 收集到的模型决策轨迹非常有用能清楚看到模型每一步观察到了什么、为什么暂停。有一次用例失败轨迹显示模型已经填好了表单但要点提交按钮时页面发生了重载把数据清空了——这显然是环境因素而非模型能力问题。5.2 提高成功率的三个关键参数第一个是task_timeout。默认值往往不够复杂流程建议单任务给到 120 秒。太短会导致模型在即将完成任务时被掐断功亏一篑。第二个是retry的语义差异。OpenClaw 支持两级重试runtime Runtime.start( profileecom_backend, env_retry1, # 环境级重试网络抖动/加载超时自动重跑整个任务 model_retry2, # 模型级重试某一步动作执行失败不重跑任务只让模型修正 )环境级重试是把整个任务重跑适合网络类问题模型级重试是让模型重新规划剩余步骤适合页面元素识别类问题。两级别分清楚能节省大量无效重跑。之前我只用全局重试一条用例失败就整体重跑浪费常是两倍时间。调整后用模型级重试解决多数问题效率提升非常明显。第三个是checkpoint中间确认。在关键步骤后增加“短暂停提取当前页面结果摘要”。比如下单流程在点击支付前先提取一次购物车金额确认无误再后续。最好是用于长流程能及早发现问题、避免执行到最后一步才报错。5.3 描述任务的语言配方我在反复调试后总结了一个任务描述模板适用于大多数 E2E 场景起点状态打开什么页面当前处于什么状态。目标动作要做哪些业务操作按顺序列出。异常分支如果出现弹窗、提示、无数据应该怎么处理。提取结果需要返回哪些字段及其格式。示例在订单列表页按金额降序排列查看第一笔订单的详情 如果详情页出现退款申请的按钮说明订单已申请退款不要点击 否则提取订单编号、订单金额、当前状态三个字段 金额保留两位小数状态用中文原文返回。我发现“顺序列表 异常分支 提取格式”三件套比一段长描述的成功率高大约 15 个百分点。描述里尽量避免模糊词汇少用“随便”“大约”多用具体操作和明确条件。5.4 日志与追踪运行完一个用例记得检查 OpenClaw 生成的三类产物截图、决策轨迹、DOM 快照。截图适合快速判断视觉层面问题决策轨迹能看到模型的思考策略是改进任务描述的直接依据DOM 快照是定位“模型看不到某元素”的利器。有一次模型反复找不到侧边栏的“财务报表”入口看快照发现侧边栏处于收起状态而任务描述里没有提到“点击展开侧边栏”。这就是典型的任务描述信息不足。5.5 资源回收与并发稳定性跑较长的测试套件时浏览器会话长时间不清理会导致内存膨胀。建议配合 pytest 的 fixture 作用域每个测试结束都老老实实调用清理别省那两三秒。并发场景下任务调度不支持一个浏览器会话同时执行两个任务OpenClaw 的会话池会自动分配你可以适当调大会话池的数量给高并发留余量。6. 拿去做 CI这套方案怎么融入流水线本地跑通只是第一步真正让这套方案产生价值的是放进 CI 流程。在流水线里Nova Act 任务选择和管道调度会遇到几个实际问题。一是权限。CI 机器通常跑在受限网络环境里要确认 Nova Act 的模型服务端口能通Artifact 存储目录可写。这些问题在本地不会遇到到了 CI 才暴露。二是超时控制。CI 总体超时和单个任务超时要拆开设置。我给整个测试阶段设置 15 分钟上限每个任务内部再自行控制。否则全局超时先触发日志信息很乱完全不知道具体卡在哪个任务。三是稳定性门禁。AI 测试天然有一定的 flaky 率直接作为发布唯一门禁容易误伤。建议做成“两次失败才拦截”的策略第一次失败自动重跑重跑通过标记为“不稳定用例”只有重跑也失败才真正阻断发布。这个策略上线后团队成员对 AI 测试的信心明显增强——用我们内部的话说没有人愿意在凌晨两点处理一个重跑就能过的红管道。四是报告输出。pytest 生成的 HTML 报告可以扩展把 OpenClaw 的截图和决策轨迹路径塞进每条用例的描述区。团队排查问题时不至于跳来跳去一份报告解决“测试脚本在哪、日志去哪看、截图哪个文件”三个问题。7. 写给后来人的实操心得这套方案跑在模拟项目 X 上面前后迭代了将近一个月。说实话第一周一直觉得不稳定——直到我意识到问题不在模型而在我的测试设计还停留在“精确告诉它每一步做什么”的旧习惯里。当你开始把测试目标当作用户故事来描述把断言目标放在业务规则上AI 自动化的优势才算真正发挥出来。如果让我给后来人几个最实用的建议大概是这些第一别把 AI 测试当黑盒。模型输出的决策轨迹一定要看那是调试任务描述的最佳线索。写成白盒思路成功率提升会快得多。第二任务描述的质量直接决定成功率。花时间打磨描述比调各种超时参数更有效。每一条用例先自己在浏览器里手工走一遍流程确认自己对页面状态的理解再把它写成任务。不要凭空写描述尤其不要假设页面上有你想象的按钮。第三坚持用结构化输出。凡是需要断言的数据都在任务描述里明确要求提取并以 JSON 返回千万别依赖页面文本去匹配。结构化返回让 pytest 的断言自洽性强的多。第四重试策略要分层。环境重试、模型重试、测试级重试三者分开不要一把梭整段重跑。分层重试不仅节省时间也更方便定位问题到底出在哪一层。第五这种方案的维护成本大头从“改选择器”转移到“改任务描述”。比起传统自动化不同维护动作的逻辑变了但维护量并没有消失。不过每次业务改版后我只需要更新对应的几行业务描述而不是翻遍整个页面层的代码——这个收益已经足够大。最后分享一下我个人在具体项目里的体会AI 驱动测试真正的适用边界是业务流程复杂、页面变动频繁、传统脚本维护已经让你崩溃的场景。如果你的页面一年都不改、结构异常稳定用 Playwright 写死反而更高效。工具本身没有优劣只有适合不适合。我选择 Nova Act 和 OpenClaw 这套组合是因为模拟项目 X 的页面改版频率实在太高传统方式已经顶不住了。如果你也在受同样的折磨不妨给这套方案一个机会。