AI 自动化测试

AI 自动化测试是噱头还是真能干?从用例生成到自愈脚本,从视觉验证到日志分析------一把拆透大模型在测试全链路上的落地姿势(附实战落地方案)

【摘要】 本文从 AI 自动化测试的落地全链路出发,拆解 LLM 在测试用例生成(需求→Gherkin→Python 用例)、Selenium/Cypress 测试脚本自动编写、失败归因分析三阶段中的实际应用。深入 AI 视觉验证(Playwright + 截图对比替代传统断言)、自愈定位器(基于语义理解动态锁定元素,避免 XPath 失效)的底层原理。包含 LLM 本地部署测试方案选型(Ollama + LLaMA 3 / Qwen 2.5)、Prompt 工程在测试中的三种调试技巧、用例准确性验证策略,以及大模型幻觉导致无效用例、Token 成本控制等踩坑记录。


目录

  • [一、AI 自动化测试到底在测什么](#一、AI 自动化测试到底在测什么)
  • [二、LLM 在测试三阶段的角色](#二、LLM 在测试三阶段的角色)
  • [三、AI 生成测试用例:从需求到可执行脚本](#三、AI 生成测试用例:从需求到可执行脚本)
  • [四、自愈定位器:XPath 坏了我自己修](#四、自愈定位器:XPath 坏了我自己修)
  • [五、AI 视觉验证:截图对比替代手工断言](#五、AI 视觉验证:截图对比替代手工断言)
  • [六、失败归因分析:AI 读日志找根因](#六、失败归因分析:AI 读日志找根因)
  • [七、本地 LLM 部署方案选型](#七、本地 LLM 部署方案选型)
  • [八、Prompt 工程在测试中的三种调试技巧](#八、Prompt 工程在测试中的三种调试技巧)
  • [九、实战:Selenium 脚本 AI 生成全流程](#九、实战:Selenium 脚本 AI 生成全流程)
  • [十、实战:Playwright + AI 视觉回归测试](#十、实战:Playwright + AI 视觉回归测试)
  • [十一、AI 测试的误区和边界](#十一、AI 测试的误区和边界)
  • 十二、总结

一、AI 自动化测试到底在测什么

传统的自动化测试长这样:
#mermaid-svg-oNNcOzKsyVCVOQEl{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oNNcOzKsyVCVOQEl .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oNNcOzKsyVCVOQEl .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oNNcOzKsyVCVOQEl .error-icon{fill:#552222;}#mermaid-svg-oNNcOzKsyVCVOQEl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oNNcOzKsyVCVOQEl .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oNNcOzKsyVCVOQEl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oNNcOzKsyVCVOQEl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oNNcOzKsyVCVOQEl .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oNNcOzKsyVCVOQEl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oNNcOzKsyVCVOQEl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oNNcOzKsyVCVOQEl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oNNcOzKsyVCVOQEl .marker.cross{stroke:#333333;}#mermaid-svg-oNNcOzKsyVCVOQEl svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oNNcOzKsyVCVOQEl p{margin:0;}#mermaid-svg-oNNcOzKsyVCVOQEl .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-oNNcOzKsyVCVOQEl .cluster-label text{fill:#333;}#mermaid-svg-oNNcOzKsyVCVOQEl .cluster-label span{color:#333;}#mermaid-svg-oNNcOzKsyVCVOQEl .cluster-label span p{background-color:transparent;}#mermaid-svg-oNNcOzKsyVCVOQEl .label text,#mermaid-svg-oNNcOzKsyVCVOQEl span{fill:#333;color:#333;}#mermaid-svg-oNNcOzKsyVCVOQEl .node rect,#mermaid-svg-oNNcOzKsyVCVOQEl .node circle,#mermaid-svg-oNNcOzKsyVCVOQEl .node ellipse,#mermaid-svg-oNNcOzKsyVCVOQEl .node polygon,#mermaid-svg-oNNcOzKsyVCVOQEl .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oNNcOzKsyVCVOQEl .rough-node .label text,#mermaid-svg-oNNcOzKsyVCVOQEl .node .label text,#mermaid-svg-oNNcOzKsyVCVOQEl .image-shape .label,#mermaid-svg-oNNcOzKsyVCVOQEl .icon-shape .label{text-anchor:middle;}#mermaid-svg-oNNcOzKsyVCVOQEl .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oNNcOzKsyVCVOQEl .rough-node .label,#mermaid-svg-oNNcOzKsyVCVOQEl .node .label,#mermaid-svg-oNNcOzKsyVCVOQEl .image-shape .label,#mermaid-svg-oNNcOzKsyVCVOQEl .icon-shape .label{text-align:center;}#mermaid-svg-oNNcOzKsyVCVOQEl .node.clickable{cursor:pointer;}#mermaid-svg-oNNcOzKsyVCVOQEl .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oNNcOzKsyVCVOQEl .arrowheadPath{fill:#333333;}#mermaid-svg-oNNcOzKsyVCVOQEl .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oNNcOzKsyVCVOQEl .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oNNcOzKsyVCVOQEl .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oNNcOzKsyVCVOQEl .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oNNcOzKsyVCVOQEl .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oNNcOzKsyVCVOQEl .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oNNcOzKsyVCVOQEl .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oNNcOzKsyVCVOQEl .cluster text{fill:#333;}#mermaid-svg-oNNcOzKsyVCVOQEl .cluster span{color:#333;}#mermaid-svg-oNNcOzKsyVCVOQEl div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-oNNcOzKsyVCVOQEl .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oNNcOzKsyVCVOQEl rect.text{fill:none;stroke-width:0;}#mermaid-svg-oNNcOzKsyVCVOQEl .icon-shape,#mermaid-svg-oNNcOzKsyVCVOQEl .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oNNcOzKsyVCVOQEl .icon-shape p,#mermaid-svg-oNNcOzKsyVCVOQEl .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oNNcOzKsyVCVOQEl .icon-shape .label rect,#mermaid-svg-oNNcOzKsyVCVOQEl .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oNNcOzKsyVCVOQEl .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oNNcOzKsyVCVOQEl .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oNNcOzKsyVCVOQEl :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 人看需求
人写用例
人写脚本

Selenium / Appium
定时跑
人看报告

判断是不是 Bug

AI 进来之后,哪些环节变了?
#mermaid-svg-6NmYsudzoH6XsbfU{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6NmYsudzoH6XsbfU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6NmYsudzoH6XsbfU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6NmYsudzoH6XsbfU .error-icon{fill:#552222;}#mermaid-svg-6NmYsudzoH6XsbfU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6NmYsudzoH6XsbfU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6NmYsudzoH6XsbfU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6NmYsudzoH6XsbfU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6NmYsudzoH6XsbfU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6NmYsudzoH6XsbfU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6NmYsudzoH6XsbfU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6NmYsudzoH6XsbfU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6NmYsudzoH6XsbfU .marker.cross{stroke:#333333;}#mermaid-svg-6NmYsudzoH6XsbfU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6NmYsudzoH6XsbfU p{margin:0;}#mermaid-svg-6NmYsudzoH6XsbfU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6NmYsudzoH6XsbfU .cluster-label text{fill:#333;}#mermaid-svg-6NmYsudzoH6XsbfU .cluster-label span{color:#333;}#mermaid-svg-6NmYsudzoH6XsbfU .cluster-label span p{background-color:transparent;}#mermaid-svg-6NmYsudzoH6XsbfU .label text,#mermaid-svg-6NmYsudzoH6XsbfU span{fill:#333;color:#333;}#mermaid-svg-6NmYsudzoH6XsbfU .node rect,#mermaid-svg-6NmYsudzoH6XsbfU .node circle,#mermaid-svg-6NmYsudzoH6XsbfU .node ellipse,#mermaid-svg-6NmYsudzoH6XsbfU .node polygon,#mermaid-svg-6NmYsudzoH6XsbfU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6NmYsudzoH6XsbfU .rough-node .label text,#mermaid-svg-6NmYsudzoH6XsbfU .node .label text,#mermaid-svg-6NmYsudzoH6XsbfU .image-shape .label,#mermaid-svg-6NmYsudzoH6XsbfU .icon-shape .label{text-anchor:middle;}#mermaid-svg-6NmYsudzoH6XsbfU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6NmYsudzoH6XsbfU .rough-node .label,#mermaid-svg-6NmYsudzoH6XsbfU .node .label,#mermaid-svg-6NmYsudzoH6XsbfU .image-shape .label,#mermaid-svg-6NmYsudzoH6XsbfU .icon-shape .label{text-align:center;}#mermaid-svg-6NmYsudzoH6XsbfU .node.clickable{cursor:pointer;}#mermaid-svg-6NmYsudzoH6XsbfU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6NmYsudzoH6XsbfU .arrowheadPath{fill:#333333;}#mermaid-svg-6NmYsudzoH6XsbfU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6NmYsudzoH6XsbfU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6NmYsudzoH6XsbfU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6NmYsudzoH6XsbfU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6NmYsudzoH6XsbfU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6NmYsudzoH6XsbfU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6NmYsudzoH6XsbfU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6NmYsudzoH6XsbfU .cluster text{fill:#333;}#mermaid-svg-6NmYsudzoH6XsbfU .cluster span{color:#333;}#mermaid-svg-6NmYsudzoH6XsbfU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6NmYsudzoH6XsbfU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6NmYsudzoH6XsbfU rect.text{fill:none;stroke-width:0;}#mermaid-svg-6NmYsudzoH6XsbfU .icon-shape,#mermaid-svg-6NmYsudzoH6XsbfU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6NmYsudzoH6XsbfU .icon-shape p,#mermaid-svg-6NmYsudzoH6XsbfU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6NmYsudzoH6XsbfU .icon-shape .label rect,#mermaid-svg-6NmYsudzoH6XsbfU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6NmYsudzoH6XsbfU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6NmYsudzoH6XsbfU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6NmYsudzoH6XsbfU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 需求文档
★ LLM 生成测试用例
★ LLM 生成脚本骨架

  • 人审改
    定时跑
    ★ AI 视觉验证

  • 失败归因
    人只看 AI 筛选过的

高价值告警

一句话: AI 不是替代整个测试流程,而是把"写用例""写脚本""看报告"这三个最吃人力、最重复的环节自动化了。

传统做法 AI 做法 效果
手写 100 条用例 LLM 从需求生成 100 条,人再审 20 分钟 节省 60% 时间
XPath 变化后手动修 自愈定位器自动找新位置 维护成本降 ~50%
人工对比截图找 UI 差异 AI 视觉模型自动对比 + 标红 回归测试效率翻倍
失败日志一条条看 AI 读日志给推断根因 定位时间从小时到分钟

二、LLM 在测试三阶段的角色

#mermaid-svg-knNdvOhzwCUObXuM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-knNdvOhzwCUObXuM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-knNdvOhzwCUObXuM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-knNdvOhzwCUObXuM .error-icon{fill:#552222;}#mermaid-svg-knNdvOhzwCUObXuM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-knNdvOhzwCUObXuM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-knNdvOhzwCUObXuM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-knNdvOhzwCUObXuM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-knNdvOhzwCUObXuM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-knNdvOhzwCUObXuM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-knNdvOhzwCUObXuM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-knNdvOhzwCUObXuM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-knNdvOhzwCUObXuM .marker.cross{stroke:#333333;}#mermaid-svg-knNdvOhzwCUObXuM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-knNdvOhzwCUObXuM p{margin:0;}#mermaid-svg-knNdvOhzwCUObXuM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-knNdvOhzwCUObXuM .cluster-label text{fill:#333;}#mermaid-svg-knNdvOhzwCUObXuM .cluster-label span{color:#333;}#mermaid-svg-knNdvOhzwCUObXuM .cluster-label span p{background-color:transparent;}#mermaid-svg-knNdvOhzwCUObXuM .label text,#mermaid-svg-knNdvOhzwCUObXuM span{fill:#333;color:#333;}#mermaid-svg-knNdvOhzwCUObXuM .node rect,#mermaid-svg-knNdvOhzwCUObXuM .node circle,#mermaid-svg-knNdvOhzwCUObXuM .node ellipse,#mermaid-svg-knNdvOhzwCUObXuM .node polygon,#mermaid-svg-knNdvOhzwCUObXuM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-knNdvOhzwCUObXuM .rough-node .label text,#mermaid-svg-knNdvOhzwCUObXuM .node .label text,#mermaid-svg-knNdvOhzwCUObXuM .image-shape .label,#mermaid-svg-knNdvOhzwCUObXuM .icon-shape .label{text-anchor:middle;}#mermaid-svg-knNdvOhzwCUObXuM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-knNdvOhzwCUObXuM .rough-node .label,#mermaid-svg-knNdvOhzwCUObXuM .node .label,#mermaid-svg-knNdvOhzwCUObXuM .image-shape .label,#mermaid-svg-knNdvOhzwCUObXuM .icon-shape .label{text-align:center;}#mermaid-svg-knNdvOhzwCUObXuM .node.clickable{cursor:pointer;}#mermaid-svg-knNdvOhzwCUObXuM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-knNdvOhzwCUObXuM .arrowheadPath{fill:#333333;}#mermaid-svg-knNdvOhzwCUObXuM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-knNdvOhzwCUObXuM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-knNdvOhzwCUObXuM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-knNdvOhzwCUObXuM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-knNdvOhzwCUObXuM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-knNdvOhzwCUObXuM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-knNdvOhzwCUObXuM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-knNdvOhzwCUObXuM .cluster text{fill:#333;}#mermaid-svg-knNdvOhzwCUObXuM .cluster span{color:#333;}#mermaid-svg-knNdvOhzwCUObXuM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-knNdvOhzwCUObXuM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-knNdvOhzwCUObXuM rect.text{fill:none;stroke-width:0;}#mermaid-svg-knNdvOhzwCUObXuM .icon-shape,#mermaid-svg-knNdvOhzwCUObXuM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-knNdvOhzwCUObXuM .icon-shape p,#mermaid-svg-knNdvOhzwCUObXuM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-knNdvOhzwCUObXuM .icon-shape .label rect,#mermaid-svg-knNdvOhzwCUObXuM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-knNdvOhzwCUObXuM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-knNdvOhzwCUObXuM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-knNdvOhzwCUObXuM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 阶段三:结果分析
失败截图 + 日志
LLM 分析根因
归类 → 报告
阶段二:测试执行
脚本跑挂了?
自愈定位器尝试修复
重跑
阶段一:测试生成
需求 / PRD
LLM 提取测试点
生成 Gherkin 用例
生成 Python / Java 脚本


三、AI 生成测试用例:从需求到可执行脚本

原始需求示例(一段手写的 PRD 描述):

用户可以在搜索框输入关键词,点击搜索按钮后,页面展示匹配结果列表。如果无匹配结果,显示"未找到相关内容"提示。搜索框为空时按钮置灰不可点击。

Step 1:让 LLM 生成 Gherkin(BDD 风格用例):

gherkin 复制代码
Feature: 搜索功能

  Scenario: 正常搜索有结果
    Given 用户打开搜索页面
    When 用户在搜索框中输入 "Python"
    And 用户点击搜索按钮
    Then 页面展示包含 "Python" 的结果列表

  Scenario: 搜索无结果时显示提示
    Given 用户打开搜索页面
    When 用户在搜索框中输入 "xyz不存在的词"
    And 用户点击搜索按钮
    Then 页面显示 "未找到相关内容"

  Scenario: 搜索框为空时按钮禁用
    Given 用户打开搜索页面
    When 搜索框为空
    Then 搜索按钮处于禁用状态,不可点击

Step 2:LLM 直接把 Gherkin 转成 Python + Playwright 脚本:

python 复制代码
from playwright.sync_api import sync_playwright, expect

def test_search_has_results():
    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page()
        page.goto("https://example.com/search")

        # 输入关键词
        page.fill("#search-input", "Python")
        # 点击搜索
        page.click("#search-btn")
        # 验证结果列表包含关键字
        expect(page.locator(".result-item").first).to_contain_text("Python")

        browser.close()

def test_empty_search_disables_button():
    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page()
        page.goto("https://example.com/search")

        # 验证搜索框为空时按钮 disabled
        expect(page.locator("#search-btn")).to_be_disabled()

        browser.close()

手写这两条用例大约 10-15 分钟,AI 从需求到可执行脚本 20 秒。剩下的时间人只需要审一遍逻辑。

生成用例的质量保障策略:

验证层 做法
格式检查 脚本能不能跑起来(语法错误 LLM 自己很少犯)
逻辑审查 人看断言对不对(AI 可能把"包含"写成"等于")
数据真实化 把 LLM 编的假数据换成业务真实数据
边界补充 LLM 一般给正常流程,你补边界:空串、超长、注入

四、自愈定位器:XPath 坏了我自己修

传统自动化测试里,维护成本最高的就是定位器。前端改个 CSS 类名,你的 //div[@class='search-wrapper'] 就挂了。

自愈定位器的思路:
#mermaid-svg-amkHIRkJfPIgV1Og{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-amkHIRkJfPIgV1Og .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-amkHIRkJfPIgV1Og .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-amkHIRkJfPIgV1Og .error-icon{fill:#552222;}#mermaid-svg-amkHIRkJfPIgV1Og .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-amkHIRkJfPIgV1Og .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-amkHIRkJfPIgV1Og .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-amkHIRkJfPIgV1Og .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-amkHIRkJfPIgV1Og .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-amkHIRkJfPIgV1Og .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-amkHIRkJfPIgV1Og .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-amkHIRkJfPIgV1Og .marker{fill:#333333;stroke:#333333;}#mermaid-svg-amkHIRkJfPIgV1Og .marker.cross{stroke:#333333;}#mermaid-svg-amkHIRkJfPIgV1Og svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-amkHIRkJfPIgV1Og p{margin:0;}#mermaid-svg-amkHIRkJfPIgV1Og .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-amkHIRkJfPIgV1Og .cluster-label text{fill:#333;}#mermaid-svg-amkHIRkJfPIgV1Og .cluster-label span{color:#333;}#mermaid-svg-amkHIRkJfPIgV1Og .cluster-label span p{background-color:transparent;}#mermaid-svg-amkHIRkJfPIgV1Og .label text,#mermaid-svg-amkHIRkJfPIgV1Og span{fill:#333;color:#333;}#mermaid-svg-amkHIRkJfPIgV1Og .node rect,#mermaid-svg-amkHIRkJfPIgV1Og .node circle,#mermaid-svg-amkHIRkJfPIgV1Og .node ellipse,#mermaid-svg-amkHIRkJfPIgV1Og .node polygon,#mermaid-svg-amkHIRkJfPIgV1Og .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-amkHIRkJfPIgV1Og .rough-node .label text,#mermaid-svg-amkHIRkJfPIgV1Og .node .label text,#mermaid-svg-amkHIRkJfPIgV1Og .image-shape .label,#mermaid-svg-amkHIRkJfPIgV1Og .icon-shape .label{text-anchor:middle;}#mermaid-svg-amkHIRkJfPIgV1Og .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-amkHIRkJfPIgV1Og .rough-node .label,#mermaid-svg-amkHIRkJfPIgV1Og .node .label,#mermaid-svg-amkHIRkJfPIgV1Og .image-shape .label,#mermaid-svg-amkHIRkJfPIgV1Og .icon-shape .label{text-align:center;}#mermaid-svg-amkHIRkJfPIgV1Og .node.clickable{cursor:pointer;}#mermaid-svg-amkHIRkJfPIgV1Og .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-amkHIRkJfPIgV1Og .arrowheadPath{fill:#333333;}#mermaid-svg-amkHIRkJfPIgV1Og .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-amkHIRkJfPIgV1Og .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-amkHIRkJfPIgV1Og .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-amkHIRkJfPIgV1Og .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-amkHIRkJfPIgV1Og .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-amkHIRkJfPIgV1Og .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-amkHIRkJfPIgV1Og .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-amkHIRkJfPIgV1Og .cluster text{fill:#333;}#mermaid-svg-amkHIRkJfPIgV1Og .cluster span{color:#333;}#mermaid-svg-amkHIRkJfPIgV1Og div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-amkHIRkJfPIgV1Og .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-amkHIRkJfPIgV1Og rect.text{fill:none;stroke-width:0;}#mermaid-svg-amkHIRkJfPIgV1Og .icon-shape,#mermaid-svg-amkHIRkJfPIgV1Og .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-amkHIRkJfPIgV1Og .icon-shape p,#mermaid-svg-amkHIRkJfPIgV1Og .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-amkHIRkJfPIgV1Og .icon-shape .label rect,#mermaid-svg-amkHIRkJfPIgV1Og .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-amkHIRkJfPIgV1Og .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-amkHIRkJfPIgV1Og .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-amkHIRkJfPIgV1Og :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 找到了
没找到
脚本执行
元素找到了?
继续执行
★ 收集当前页面 DOM 快照
传给 LLM

给原始定位器 + 当前 DOM
LLM 分析语义

找出最可能的替代元素
生成新定位器

自动更新测试脚本
重试执行

LLM 自愈定位器的 Prompt 示例:

复制代码
原始定位器:#search-btn 未找到。
当前页面 DOM 摘要:
<button class="search-submit" id="search-submit-btn" data-testid="search-trigger">
  搜索
</button>

请找出对应的新定位器,返回 JSON:
{"strategy": "css", "value": "..."}

LLM 返回:

json 复制代码
{"strategy": "css", "value": "button[data-testid='search-trigger']"}

脚本自动把定位器换成新的,重新执行------整个过程不需要人介入。

自愈成功率(实测数据参考):

场景 成功率 说明
单纯换 CSS 类名 ~95% LLM 语义匹配很强
整个 DOM 结构重构 ~40% 变化太大,定位器没语义锚点
加个 data-testid ~100% 最稳定的方式

五、AI 视觉验证:截图对比替代手工断言

有些东西不适合用定位器断言------比如"这个按钮的 icon 有没有被截断""这个页面的整体 UI 有没有变形"。

传统做法: 人工截图对比,肉眼找差异。

AI 做法:
#mermaid-svg-9u2andP4jUiPXOxq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9u2andP4jUiPXOxq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9u2andP4jUiPXOxq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9u2andP4jUiPXOxq .error-icon{fill:#552222;}#mermaid-svg-9u2andP4jUiPXOxq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9u2andP4jUiPXOxq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9u2andP4jUiPXOxq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9u2andP4jUiPXOxq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9u2andP4jUiPXOxq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9u2andP4jUiPXOxq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9u2andP4jUiPXOxq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9u2andP4jUiPXOxq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9u2andP4jUiPXOxq .marker.cross{stroke:#333333;}#mermaid-svg-9u2andP4jUiPXOxq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9u2andP4jUiPXOxq p{margin:0;}#mermaid-svg-9u2andP4jUiPXOxq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9u2andP4jUiPXOxq .cluster-label text{fill:#333;}#mermaid-svg-9u2andP4jUiPXOxq .cluster-label span{color:#333;}#mermaid-svg-9u2andP4jUiPXOxq .cluster-label span p{background-color:transparent;}#mermaid-svg-9u2andP4jUiPXOxq .label text,#mermaid-svg-9u2andP4jUiPXOxq span{fill:#333;color:#333;}#mermaid-svg-9u2andP4jUiPXOxq .node rect,#mermaid-svg-9u2andP4jUiPXOxq .node circle,#mermaid-svg-9u2andP4jUiPXOxq .node ellipse,#mermaid-svg-9u2andP4jUiPXOxq .node polygon,#mermaid-svg-9u2andP4jUiPXOxq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9u2andP4jUiPXOxq .rough-node .label text,#mermaid-svg-9u2andP4jUiPXOxq .node .label text,#mermaid-svg-9u2andP4jUiPXOxq .image-shape .label,#mermaid-svg-9u2andP4jUiPXOxq .icon-shape .label{text-anchor:middle;}#mermaid-svg-9u2andP4jUiPXOxq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9u2andP4jUiPXOxq .rough-node .label,#mermaid-svg-9u2andP4jUiPXOxq .node .label,#mermaid-svg-9u2andP4jUiPXOxq .image-shape .label,#mermaid-svg-9u2andP4jUiPXOxq .icon-shape .label{text-align:center;}#mermaid-svg-9u2andP4jUiPXOxq .node.clickable{cursor:pointer;}#mermaid-svg-9u2andP4jUiPXOxq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9u2andP4jUiPXOxq .arrowheadPath{fill:#333333;}#mermaid-svg-9u2andP4jUiPXOxq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9u2andP4jUiPXOxq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9u2andP4jUiPXOxq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9u2andP4jUiPXOxq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9u2andP4jUiPXOxq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9u2andP4jUiPXOxq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9u2andP4jUiPXOxq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9u2andP4jUiPXOxq .cluster text{fill:#333;}#mermaid-svg-9u2andP4jUiPXOxq .cluster span{color:#333;}#mermaid-svg-9u2andP4jUiPXOxq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9u2andP4jUiPXOxq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9u2andP4jUiPXOxq rect.text{fill:none;stroke-width:0;}#mermaid-svg-9u2andP4jUiPXOxq .icon-shape,#mermaid-svg-9u2andP4jUiPXOxq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9u2andP4jUiPXOxq .icon-shape p,#mermaid-svg-9u2andP4jUiPXOxq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9u2andP4jUiPXOxq .icon-shape .label rect,#mermaid-svg-9u2andP4jUiPXOxq .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9u2andP4jUiPXOxq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9u2andP4jUiPXOxq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9u2andP4jUiPXOxq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 基线截图

(正确 UI)
多模态 LLM

GPT-4V / Qwen-VL
当前截图

(被测 UI)
返回差异报告

文字描述 + 差异区域坐标
自动判定

Pass / Fail

Playwright + AI 视觉验证代码示例:

python 复制代码
from playwright.sync_api import sync_playwright
import base64

def ai_visual_check(page, baseline_path, current_path, model):
    # 1. 截图当前页面
    page.screenshot(path=current_path, full_page=True)
    
    # 2. 把两张图编码成 base64 喂给多模态 LLM
    with open(baseline_path, 'rb') as f:
        baseline_b64 = base64.b64encode(f.read()).decode()
    with open(current_path, 'rb') as f:
        current_b64 = base64.b64encode(f.read()).decode()
    
    # 3. 调多模态 API 比较
    prompt = """
    比较基线截图和当前截图,检查:
    1. 布局有没有位移或变形
    2. 文字有没有截断或重叠
    3. 颜色/间距有没有明显差异
    4. 按钮/输入框有没有缺失
    
    返回 JSON:
    {"passed": bool, "issues": [{"desc": "...", "severity": "high|medium|low"}]}
    """
    # response = call_vision_llm(prompt, baseline_b64, current_b64)
    # return response

适用场景 vs 不适用场景:

适用 AI 视觉验证 不适用(用传统断言)
UI 整体布局回归 数值精确校验
icon / 图片渲染 API 返回数据断言
多分辨率适配 数据库一致性检查
暗黑模式切换 性能指标(响应时间)

六、失败归因分析:AI 读日志找根因

跑了几百条用例挂了 10 条,传统做法是一条条点开报告、看截图、翻日志------半小时起步。

AI 做法------把失败日志和截图丢给 LLM,让它做第一轮归因:

python 复制代码
def analyze_failure(test_name, error_log, screenshot_b64):
    prompt = f"""
    测试用例「{test_name}」失败。
    错误日志:
    {error_log}
    
    请分析根因,输出 JSON:
    {{
        "root_cause": "元素未找到 | 超时 | API 错误 | 数据问题 | 环境问题 | 脚本错误",
        "confidence": 0.0~1.0,
        "explanation": "中文解释",
        "suggested_fix": "修复建议"
    }}
    """
    # response = call_llm(prompt)
    # return response

AI 归因分类长这样:

根因分类 示例
元素未找到 前端改了 DOM,定位器失效
超时 页面加载变慢,timeout 不够
API 错误 后端返回 500,前端没兜住
数据问题 测试账号过期 / 订单数据被清
环境问题 测试环境挂了 / 网络不通
脚本错误 参数传错 / 断言写错

七、本地 LLM 部署方案选型

敏感数据不能上 OpenAI------那就本地跑:

方案 模型 硬件需求 适用
Ollama + Qwen 2.5 (7B) 通义千问 16GB 内存 / 无 GPU 也能跑 用例生成
Ollama + LLaMA 3.1 (8B) Meta 同上 脚本生成
vLLM + Qwen 2.5 (32B) 通义千问 24GB+ 显存 视觉验证
OpenAI API (GPT-4o) OpenAI 无本地硬件需求 效果最好但数据可能泄露

本地部署命令(Ollama 一行搞定):

bash 复制代码
# 安装 Ollama
curl -fsSL https://ollama.com/install.sh | sh

# 拉 Qwen 2.5 7B
ollama pull qwen2.5:7b

# 本地跑起来
ollama run qwen2.5:7b

八、Prompt 工程在测试中的三种调试技巧

技巧一:给出正例 + 反例

text 复制代码
生成测试用例时:
- 正例:正常登录成功
- 反例:密码错误登录失败
- 边界:密码为空点击登录按钮

技巧二:约束输出格式

复制代码
输出格式要求:
每条用例一行,格式为 [模块]-[场景]-[预期结果]
不要输出 Markdown 表格,直接输出纯文本列表。

技巧三:让 LLM 自己先审一遍

复制代码
先根据需求生成 5 条测试用例,然后你自己检查一遍:
1. 有没有重复的用例?
2. 有没有遗漏的需求点?
3. 有没有不合理的预期?

检查完成后,把修正后的用例单独输出在"---"分隔线下方。

九、实战:Selenium 脚本 AI 生成全流程

完整流程------从需求到可跑脚本:

python 复制代码
# 模拟一条完整的 AI 生成链路
# 输入:一段自然语言需求
# 输出:可执行的 Selenium 脚本

import openai

requirement = """
登录页面:
1. 正确用户名 + 正确密码 → 跳转到首页
2. 正确用户名 + 错误密码 → 提示"密码错误",停留在登录页
3. 连续 3 次密码错误 → 锁定账户 15 分钟,提示"账户已锁定"
"""

prompt = f"""
根据以下需求生成 Python Selenium 测试脚本:
{requirement}

要求:
- 使用 pytest 框架
- 显式等待用 WebDriverWait + expected_conditions
- 每个场景一个 test_ 函数
- 代码注释用中文
"""

# response = openai.ChatCompletion.create(
#     model="gpt-4o",
#     messages=[{"role": "user", "content": prompt}]
# )

AI 生成的脚本(审改后):

python 复制代码
import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

@pytest.fixture
def driver():
    d = webdriver.Chrome()
    d.get("http://localhost:3000/login")
    yield d
    d.quit()

def test_login_success(driver):
    # 输入正确的用户名和密码
    driver.find_element(By.ID, "username").send_keys("admin")
    driver.find_element(By.ID, "password").send_keys("pass123")
    driver.find_element(By.ID, "login-btn").click()
    # 验证跳转到首页
    WebDriverWait(driver, 10).until(
        EC.url_contains("/home")
    )

def test_login_wrong_password(driver):
    driver.find_element(By.ID, "username").send_keys("admin")
    driver.find_element(By.ID, "password").send_keys("wrong123")
    driver.find_element(By.ID, "login-btn").click()
    # 验证错误提示
    error_msg = WebDriverWait(driver, 10).until(
        EC.visibility_of_element_located((By.CLASS_NAME, "error-msg"))
    )
    assert "密码错误" in error_msg.text
    # 验证没有跳转
    assert "/login" in driver.current_url

def test_account_lockout(driver):
    # 连续 3 次输入错误密码
    for _ in range(3):
        driver.find_element(By.ID, "username").clear()
        driver.find_element(By.ID, "password").clear()
        driver.find_element(By.ID, "username").send_keys("admin")
        driver.find_element(By.ID, "password").send_keys("wrong123")
        driver.find_element(By.ID, "login-btn").click()
    # 验证锁定提示
    lock_msg = WebDriverWait(driver, 10).until(
        EC.visibility_of_element_located((By.CLASS_NAME, "error-msg"))
    )
    assert "账户已锁定" in lock_msg.text

这段脚本 AI 生成 20 秒,人审改 2 分钟。审改主要做的:把 AI 编的假元素 ID 换成真实 DOM 中的 ID。


十、实战:Playwright + AI 视觉回归测试

python 复制代码
from playwright.sync_api import sync_playwright
import json

def visual_regression_with_ai():
    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page()
        page.goto("http://localhost:3000/dashboard")

        # 截图基线(第一次跑时存下来)
        baseline = "baseline_dashboard.png"
        current = "current_dashboard.png"
        # page.screenshot(path=baseline, full_page=True)  # 第一次

        page.screenshot(path=current, full_page=True)

        # 传给 AI 视觉模型对比
        # result = ai_visual_diff(baseline, current)
        # 预期返回:
        # {
        #   "passed": false,
        #   "differences": [
        #     {"area": "右上角用户头像",
        #      "desc": "头像从圆形变成了方形,圆角丢失",
        #      "severity": "low"},
        #     {"area": "左侧导航栏",
        #      "desc": "导航栏整体宽度增加了约 15px",
        #      "severity": "medium"}
        #   ]
        # }

        browser.close()

十一、AI 测试的误区和边界

误区一:AI 可以完全替代测试人员

不能。AI 生成用例的质量取决于需求写得多清楚。需求里没写的边界条件,AI 不会主动补。而且 AI 不知道业务上下文------它不知道哪些字段跟钱相关,哪些场景出了问题会赔钱。

误区二:用例越多越好

LLM 可以一口气吐出 200 条用例------但里面有 30% 是废话或重复。审改成本不能省。

误区三:API 调一次就完事了
#mermaid-svg-SQ1BIwSvSxJyJmUu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-SQ1BIwSvSxJyJmUu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SQ1BIwSvSxJyJmUu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SQ1BIwSvSxJyJmUu .error-icon{fill:#552222;}#mermaid-svg-SQ1BIwSvSxJyJmUu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SQ1BIwSvSxJyJmUu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SQ1BIwSvSxJyJmUu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SQ1BIwSvSxJyJmUu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SQ1BIwSvSxJyJmUu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SQ1BIwSvSxJyJmUu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SQ1BIwSvSxJyJmUu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SQ1BIwSvSxJyJmUu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SQ1BIwSvSxJyJmUu .marker.cross{stroke:#333333;}#mermaid-svg-SQ1BIwSvSxJyJmUu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SQ1BIwSvSxJyJmUu p{margin:0;}#mermaid-svg-SQ1BIwSvSxJyJmUu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-SQ1BIwSvSxJyJmUu .cluster-label text{fill:#333;}#mermaid-svg-SQ1BIwSvSxJyJmUu .cluster-label span{color:#333;}#mermaid-svg-SQ1BIwSvSxJyJmUu .cluster-label span p{background-color:transparent;}#mermaid-svg-SQ1BIwSvSxJyJmUu .label text,#mermaid-svg-SQ1BIwSvSxJyJmUu span{fill:#333;color:#333;}#mermaid-svg-SQ1BIwSvSxJyJmUu .node rect,#mermaid-svg-SQ1BIwSvSxJyJmUu .node circle,#mermaid-svg-SQ1BIwSvSxJyJmUu .node ellipse,#mermaid-svg-SQ1BIwSvSxJyJmUu .node polygon,#mermaid-svg-SQ1BIwSvSxJyJmUu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SQ1BIwSvSxJyJmUu .rough-node .label text,#mermaid-svg-SQ1BIwSvSxJyJmUu .node .label text,#mermaid-svg-SQ1BIwSvSxJyJmUu .image-shape .label,#mermaid-svg-SQ1BIwSvSxJyJmUu .icon-shape .label{text-anchor:middle;}#mermaid-svg-SQ1BIwSvSxJyJmUu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-SQ1BIwSvSxJyJmUu .rough-node .label,#mermaid-svg-SQ1BIwSvSxJyJmUu .node .label,#mermaid-svg-SQ1BIwSvSxJyJmUu .image-shape .label,#mermaid-svg-SQ1BIwSvSxJyJmUu .icon-shape .label{text-align:center;}#mermaid-svg-SQ1BIwSvSxJyJmUu .node.clickable{cursor:pointer;}#mermaid-svg-SQ1BIwSvSxJyJmUu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-SQ1BIwSvSxJyJmUu .arrowheadPath{fill:#333333;}#mermaid-svg-SQ1BIwSvSxJyJmUu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SQ1BIwSvSxJyJmUu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SQ1BIwSvSxJyJmUu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SQ1BIwSvSxJyJmUu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-SQ1BIwSvSxJyJmUu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SQ1BIwSvSxJyJmUu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-SQ1BIwSvSxJyJmUu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SQ1BIwSvSxJyJmUu .cluster text{fill:#333;}#mermaid-svg-SQ1BIwSvSxJyJmUu .cluster span{color:#333;}#mermaid-svg-SQ1BIwSvSxJyJmUu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-SQ1BIwSvSxJyJmUu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-SQ1BIwSvSxJyJmUu rect.text{fill:none;stroke-width:0;}#mermaid-svg-SQ1BIwSvSxJyJmUu .icon-shape,#mermaid-svg-SQ1BIwSvSxJyJmUu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SQ1BIwSvSxJyJmUu .icon-shape p,#mermaid-svg-SQ1BIwSvSxJyJmUu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-SQ1BIwSvSxJyJmUu .icon-shape .label rect,#mermaid-svg-SQ1BIwSvSxJyJmUu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SQ1BIwSvSxJyJmUu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-SQ1BIwSvSxJyJmUu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-SQ1BIwSvSxJyJmUu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 没
全了
需求 → LLM → 用例
人审改
跑一遍
看覆盖率
覆盖全了?
补需求 → 再生成
入库

成本边界------自己心里有个数:

调用方式 成本估算 适合
OpenAI GPT-4o API ~$0.0025 / 用例 小批量用
本地 Qwen 2.5 7B 电费,几乎免费 大批量日常跑
开源视觉模型 GPU 占用 ~8GB 显存 视觉回归

十二、总结

AI 测试全链路一览:
#mermaid-svg-m6n3sqGZCDRqstfk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-m6n3sqGZCDRqstfk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-m6n3sqGZCDRqstfk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-m6n3sqGZCDRqstfk .error-icon{fill:#552222;}#mermaid-svg-m6n3sqGZCDRqstfk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-m6n3sqGZCDRqstfk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-m6n3sqGZCDRqstfk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-m6n3sqGZCDRqstfk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-m6n3sqGZCDRqstfk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-m6n3sqGZCDRqstfk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-m6n3sqGZCDRqstfk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-m6n3sqGZCDRqstfk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-m6n3sqGZCDRqstfk .marker.cross{stroke:#333333;}#mermaid-svg-m6n3sqGZCDRqstfk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-m6n3sqGZCDRqstfk p{margin:0;}#mermaid-svg-m6n3sqGZCDRqstfk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-m6n3sqGZCDRqstfk .cluster-label text{fill:#333;}#mermaid-svg-m6n3sqGZCDRqstfk .cluster-label span{color:#333;}#mermaid-svg-m6n3sqGZCDRqstfk .cluster-label span p{background-color:transparent;}#mermaid-svg-m6n3sqGZCDRqstfk .label text,#mermaid-svg-m6n3sqGZCDRqstfk span{fill:#333;color:#333;}#mermaid-svg-m6n3sqGZCDRqstfk .node rect,#mermaid-svg-m6n3sqGZCDRqstfk .node circle,#mermaid-svg-m6n3sqGZCDRqstfk .node ellipse,#mermaid-svg-m6n3sqGZCDRqstfk .node polygon,#mermaid-svg-m6n3sqGZCDRqstfk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-m6n3sqGZCDRqstfk .rough-node .label text,#mermaid-svg-m6n3sqGZCDRqstfk .node .label text,#mermaid-svg-m6n3sqGZCDRqstfk .image-shape .label,#mermaid-svg-m6n3sqGZCDRqstfk .icon-shape .label{text-anchor:middle;}#mermaid-svg-m6n3sqGZCDRqstfk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-m6n3sqGZCDRqstfk .rough-node .label,#mermaid-svg-m6n3sqGZCDRqstfk .node .label,#mermaid-svg-m6n3sqGZCDRqstfk .image-shape .label,#mermaid-svg-m6n3sqGZCDRqstfk .icon-shape .label{text-align:center;}#mermaid-svg-m6n3sqGZCDRqstfk .node.clickable{cursor:pointer;}#mermaid-svg-m6n3sqGZCDRqstfk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-m6n3sqGZCDRqstfk .arrowheadPath{fill:#333333;}#mermaid-svg-m6n3sqGZCDRqstfk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-m6n3sqGZCDRqstfk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-m6n3sqGZCDRqstfk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-m6n3sqGZCDRqstfk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-m6n3sqGZCDRqstfk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-m6n3sqGZCDRqstfk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-m6n3sqGZCDRqstfk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-m6n3sqGZCDRqstfk .cluster text{fill:#333;}#mermaid-svg-m6n3sqGZCDRqstfk .cluster span{color:#333;}#mermaid-svg-m6n3sqGZCDRqstfk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-m6n3sqGZCDRqstfk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-m6n3sqGZCDRqstfk rect.text{fill:none;stroke-width:0;}#mermaid-svg-m6n3sqGZCDRqstfk .icon-shape,#mermaid-svg-m6n3sqGZCDRqstfk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-m6n3sqGZCDRqstfk .icon-shape p,#mermaid-svg-m6n3sqGZCDRqstfk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-m6n3sqGZCDRqstfk .icon-shape .label rect,#mermaid-svg-m6n3sqGZCDRqstfk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-m6n3sqGZCDRqstfk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-m6n3sqGZCDRqstfk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-m6n3sqGZCDRqstfk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 分析
执行
生成
需求 → LLM → 用例
用例 → LLM → 脚本
定时跑脚本
自愈定位器兜底
失败截图 + 日志
LLM 归因
分类成报告

核心速查:

你要做什么 推荐工具 / 方案
从需求生成用例 GPT-4o / 本地 Qwen 2.5
用例生成脚本 LLM + Playwright / Selenium
定位器自愈 LLM 语义匹配 DOM
视觉回归 GPT-4V / Qwen-VL 截图对比
失败归因 LLM 读日志 + 截图
本地部署 Ollama + Qwen 2.5 7B

一句话总结: AI 在测试里的正确使用姿势是------它写初稿,你来审改。用例生成、脚本骨架、日志分析这些最耗时间的脏活给 AI,业务判断、边界补充、质量把关留给人。现阶段别指望 AI 全自动------但它帮你省掉 50%~60% 的重复劳动是完全做得到的。

相关推荐
抓蛙师1 小时前
【无标题】
人工智能
愚公搬代码1 小时前
【愚公系列】《移动端AI应用开发》013-DeepSeek API开发与集成(深度集成与中间件架构)
人工智能·中间件·架构
段一凡-华北理工大学1 小时前
工业领域的Hadoop架构学习~系列文章17:Hadoop性能调优- 调度集群每一分性能
大数据·人工智能·hadoop·分布式·学习·架构·高炉炼铁
feiwuw1 小时前
Hermes Agent介绍
人工智能·hermes
故渊at1 小时前
第一板块:Android 系统基石与运行原理 | 第五篇:Context 上下文与资源配置体系
android·人工智能·opencv·context·上下文·资源配置体系
ice8130331811 小时前
【Python】调用opencv识别图片人脸位置
人工智能·python·opencv
KaMeidebaby1 小时前
卡梅德生物技术快报|蛋白定制:ACE 抑制肽原辅料工艺全参数|适配蛋白定制的提取 & 酶解标准化实操手册
大数据·人工智能·架构·spark·新浪微博
团象科技1 小时前
中小出海团队运维观察:WordPress站点境外云环境搭建实操路径梳理
大数据·运维·人工智能
沐籽李1 小时前
Proteina-Complexa:NVIDIA 如何把蛋白 Binder 设计推进到全原子生成时代?
大数据·人工智能·算法·英伟达·蛋白质生成