
最开始聊这个话题是因为团队里一年维护了三千多条UI自动化用例每次前端改版光修选择器和定位逻辑就能占掉测试组一半的工作量。后来我们开始尝试把大模型接进测试生成流程做成了一个内部原型平台虽然谈不上颠覆但确实让很多以前靠人肉堆的环节变得不再可怕。这篇文章不打算讲那些虚的“AI赋能”故事而是把我在设计“AI驱动UI自动化测试生成平台”时真正踩过的坑、验证过的方案、以及反复推翻过的假设尽量原原本本讲清楚。这个平台的核心一句话就能概括让AI根据页面的真实结构和人的自然语言描述自动生成可执行的UI自动化测试代码并在运行失败后主动分析与修复。它适合已经受够了纯手写Selenium/Playwright脚本的测试团队也适合正在犹豫“要不要引入AI、从哪里开始引入”的技术管理者。下面这些内容没有固定结论更多是探索过程中的思考沉淀你可以把它当一份方案草图也可以当一张排雷地图。1. 为什么要把AI搬进UI自动化测试先认清要解决的真问题1.1 传统UI自动化测试的尴尬现状我见过的绝大多数UI自动化测试团队都逃不开三个老问题用例编写慢、元素定位脆、维护成本高。一个熟练的测试开发写一条简单的登录用例可能只需要十分钟但遇到复杂的业务流比如多步表单、跨页面跳转、动态列表一上午能磨出两三条就算不错。更让人绝望的是这些脚本写完只是开始前端只要改了class名、调整了DOM结构、或者把静态加载改成异步渲染对应的用例就会在一夜之间全部飘红。从数学上看这个账更扎心。假设团队有500条UI用例每条用例平均包含20个操作步骤每步涉及的定位器或等待逻辑可能都需要随前端变动而维护。按每次前端改版影响20%的用例计算一个版本就要修100条用例每条按30分钟估算就是50个小时的纯手工维护量。这个数字随着用例规模增长几乎是指数级的——因为前端组件化和频繁迭代受影响的范围往往不是线性扩展的。传统的解决办法也尝试过很多提高选择器编写规范、推广Page Object模式、引入视觉回归测试、加强Code Review……这些手段都很有效但它们没有改变一个本质事实测试代码仍然是由人逐行写出来的人的精力上限就是维护成本的上限。这也是我最初思考“AI能不能介入”的起点。1.2 AI能切入的三个关键节点把AI放进UI自动化测试不是让AI取代整个流程而是要看清楚流程里最消耗人的环节集中在哪几个点。我的判断是三个节点最有价值第一页面理解和结构化描述。人写脚本前要先搞清楚页面上有哪些可操作元素、它们的语义是什么、彼此之间是什么关系。这个过程完全可以由AI代劳——给AI一份可访问的页面快照让它抽取出关键元素列表和交互方式。第二自然语言到代码的翻译。测试用例本质上是一个“输入、操作、断言”的序列。这个序列如果用自然语言表达比如“打开首页输入用户名点击登录验证跳转到工作台”AI可以很稳定地转换成Playwright或Selenium代码。这一步解决了编写速度的瓶颈。第三失败后的自动诊断与修复。脚本跑挂之后以往都是人工去看日志、截图、定位原因。AI可以结合执行日志、页面截图和DOM快照判断是定位器失效、页面结构变化、还是真实的功能回归并尝试自动生成修复补丁。这三个节点恰好对应传统UI自动化的主要痛写得慢、定位难、维护累。平台的架构设计也基本是围绕这三个节点展开的。1.3 我们需要的是一个“生成平台”而不是一堆脚本在探索初期我见过两种走偏的做法。一种是买一个商业化工具用它的AI生成能力跑几条演示用例看起来很惊艳但深入用就会发现封闭得厉害——不能对接自有的测试框架不能定制Prompt生成的代码质量也没法人工干预。另一种是自研一堆互相独立的脚本比如一个专门负责生成登录脚本一个专门负责生成查询脚本各自为政没有统一的数据格式和反馈回路。这两种做法的问题都在于没有“平台思维”。真正值得做的是一个闭环平台从“页面提取”到“用例生成”再到“执行反馈”到“自动修复”每一层的能力可以被上层统一调度每一层的结果也可以被下层复用。哪怕一开始做得很轻这个架构方向也应该立住。否则AI生成能力再强也只是一堆散装的自动化脚本没法规模化复制。2. 平台整体架构从“录脚本”到“生成-执行-自愈”的闭环设计2.1 平台能力分层拆解我设计这个平台时把能力拆成了四层每一层只做一件事层与层之间通过标准化的数据接口通信。第一层是页面解析层。负责从浏览器运行时环境里抓取当前页面的DOM树、元素属性、可访问性信息比如aria-label、以及页面截图。这里的关键不是简单地把HTML转成文本而是把信息整理成LLM容易理解的格式并且去掉干扰项——比如隐藏元素、脚本注入的无关节点、动态生成的样式类名。这一层输出的是页面语义快照Page Semantic Snapshot。第二层是生成编排层。负责接收用户的自然语言操作描述结合页面语义快照和项目里沉淀的历史用例模板生成测试步骤序列再翻译成可执行的测试代码。这一层是核心后面会重点展开。它要同时管住代码的正确性、选择器的稳定性、断言的合理性以及生成过程的可干预性。第三层是执行引擎层。负责把生成的代码放到真实的浏览器环境里去跑收集执行日志、截图、网络请求、控制台报错、以及步骤级的通过/失败标记。为了照顾不同的技术栈我建议做执行器适配层——同样的用例描述既可以被编译成Playwright代码也可以被编译成Selenium代码。第四层是反馈自愈层。这是最有价值但最容易被忽视的一层。执行失败后不能只把错误抛给用户而要自动做诊断丢给LLM“执行日志历史快照现场快照失败断言”让它分析原因并给出修复建议或直接生成补丁。补丁需要经过二次校验比如先在无头模式里跑一遍才能正式合入用例库。2.2 核心执行链路怎么串起来四层能力如何串成一条完整链路我画过一张时序图这里用文字复述一下用户在平台上提交一个需求比如“我想验证用户能通过邮箱找回密码”平台先让浏览器打开待测页面生成页面语义快照然后调用生成编排层产出候选用例用户可以在线查看并修改确认后交给执行引擎跑如果跑挂了平台自动进入诊断模式结合现场数据给出修复建议修复后的脚本重新执行全部通过后归档为正式用例。这条链路的关键在于每一步都给用户留出干预接口。AI生成不是一次到底的魔法而是“边生成边确认边修正”的迭代过程。第一次生成的脚本可能只有七十分但是用户改一行、AI再补一步很快就能达到可入库的标准。这种“人机协同”的交互设计比追求全自动生成的炫技要实用得多。2.3 工具链选型思考LLM接入、浏览器自动化框架、数据存储关于工具选型我没有特别迷信某个产品反而更看重适配面。LLM接入层面不同团队能用的模型不一样有的有私有化部署需求有的只能调云端API。平台设计时要抽象出一层模型网关让上层代码不依赖具体模型同一个Prompt可以无缝跑在不同模型上。这个抽象成本很低但能保住未来的灵活性。浏览器自动化框架我建议首选Playwright原因是它的现代浏览器支持、自动等待机制和丰富的Debug工具都更适合AI生成场景。但平台不能只绑死一个框架所以下层要做代码生成适配器——目前内部有Playwright和Selenium两套后端以验证同一个生成逻辑在不同框架下的稳定性。数据存储方面除了传统的用例信息、执行记录还需要存页面快照的历史版本、元素属性的变化轨迹、AI生成过程中的Prompt与响应日志。这些数据是后续做智能化分析的基础。比如你能统计“哪个页面区域最容易导致AI生成失败”“哪类需求描述最容易让模型产生歧义”。3. 核心细节解析测试用例生成的三个关键环节3.1 页面元素识别给LLM一双能看到网页的“眼睛”我踩过的最大的坑是天真地认为“让AI直接看HTML源代码就能写脚本”。第一次尝试时我把整段DOM丢给模型结果模型输出的选择器可笑至极——它试图定位一个很深的span标签但那其实只是一个普通的文本节点根本无法稳定操作。后来我意识到浏览器里的自动化不是AI在看而是AI借助工具在看。正确做法是平台借助浏览器能力把页面里的可交互元素主动提取出来按层次和组织关系整理成一个结构化的JSON快照。比如一个输入框快照里不仅要有它的placeholder、type、id还要有它所属的表单区域、可见状态、是否有前置校验规则。再比如一个按钮快照里要标明这是主导航、操作按钮还是链接样式以及它触发后可能产生的行为。这样做的效果是让模型不需要从一堆CSS选择器里“猜”而是基于一个已经被提纯过的信息集合做推理。生成出来的代码自然不再瞎猜选择器而是直接引用快照里带有的稳定属性比如可访问名称、组件数据埋点、或者平台预设的业务ID。用大白话讲传统方式就像是让AI从一整面杂乱的信息墙中自己找路标而我们的方式是先给这面墙刷好分区、装好灯光再让AI去指路。3.2 从需求/操作描述到测试脚本Prompt工程的实战设计Prompt是生成质量的分水岭。我见过很多团队直接写一句“帮我写个自动化脚本”得到的结果当然不堪入目。实际生产环境下的Prompt至少要包含四个要素角色定义、页面快照、操作要求与生成规范、示例模板。角色定义建议这样写“你是一名资深测试开发工程师熟悉Playwright或Selenium擅长编写高稳定性的UI自动化测试代码。”这不是废话它决定了模型输出的代码风格、注释习惯、异常处理方式。页面快照是上下文的核心但要注意控制Token量。一整棵复杂的DOM树可能上万Token直接塞给模型又慢又贵。我的经验是提前做裁剪只保留当前可视区域内的可交互元素并且把长文本值用占位符代替。这样既保住了信息密度又能把Token控制在合理范围。一份中等复杂页面的快照我控制在1500-2500个Token左右这是平衡效果和成本比较好的区间。操作要求部分要把约束写清楚比如“必须使用data-testid作为首选定位器禁止使用动态生成的样式类名所有交互必须配合等待条件输出格式参照给定的模板类。”这些条条框框不是限制AI而是在帮助AI排除大量无效输出。示例模板是让AI“开窍”最快的方式。与其讲一堆抽象规范不如给它一条写得很好的用例并标注“注意选择器如何选择、等待如何实现、断言如何设计”。几次实验下来我确认了一个规律一个高质量示例的引导效果远大于十条抽象规则。3.3 选择器稳定性所有AI生成脚本的第一道坎选择器稳定性是AI生成脚本与人工编写脚本最本质的差距所在。人工写的脚本可能也会有脆弱的XPath但你会有意识地避开明显会变的东西AI如果只凭页面快照生成非常容易执着于某个特定的class名或某个层级很深的路径。为了压这个问题我在平台里做了两层处理。第一层是快照阶段就给选择性标注稳定性权值对于明显是框架生成的hash样式类名、或者没有语义的嵌套div直接降低其在候选列表里的优先级而对data-testid、稳定id、可访问名称、语义化标签这些给高优先级。这相当于事前给模型灌输了一个“什么选择器更可靠”的偏好。第二层是生成后的静态检查。平台会对生成的代码做一次AST分析找出所有使用CSS类名或绝对路径的定位器并给出风险警告提示用户改为更稳定的方式。这一步在传统人工开发里没有但对AI生成场景至关重要——因为它把质量问题拦截在进库之前而不是等跑挂了再修。实验数据我们内部统计显示加上这层检查后首次生成用例的稳定通过率大约提高了近四成。3.4 分层生成策略单步操作、业务流、断言设计生成策略上我强烈建议不要“一口吃成一个胖子”而是把用例生成拆成三个层次单步操作生成、业务流组装、断言补充与校验。单步操作生成是最小的原子单元比如“在登录框输入name”、“点击登录按钮”。这个层次上的生成难度较低模型只要根据快照里一个元素的属性和周边环境输出一到两行交互代码即可。因为上下文粒度小出错的概率也低适合用来验证平台的链路是否通顺。业务流组装是把多个原子操作串成一个完整的故事。这个层次容易出现两类问题一是步骤之间的等待和路由关系不清晰二是有些步骤依赖前一步的返回值比如提取短信验证码模型容易“想当然”地假设。处理办法是引入“步骤依赖图”在生成后校验每步之间的上下文继承关系发现断链就触发一次补充生成或人工确认。断言设计是最容易被忽略但重要性最高的环节。很多团队用AI生成了几十步操作却只有一个“页面标题包含XXX”的断言这等于跑了个锤子。我的做法是要求模型在生成完操作后必须独立提出一组断言建议覆盖页面跳转与路由变化、关键元素的出现与消失、数据值的正确性、异常提示的有无。然后再由平台或用户挑选真正有效的断言而不是照单全收。这样生成的用例才有“测试”的意义而不只是“巡检”。4. 实操过程与核心环节实现从零搭一个最小可用平台4.1 页面语义快照的实现思路先说明一点页面语义快照不是一个静态的HTML抓取它要把浏览器运行时里动态计算出来的信息一起打包。我可以给出一个最小实现的思路你本地也能很快跑起来。借助Playwright的CDPChrome DevTools Protocol能力在页面加载完成后抓取DOM并用JavaScript侧的逻辑做一次信息抽取。比如在这段代码里遍历页面上的button、input、a[href]、select这些交互元素提取它们的标签文本、name、aria-label、placeholder、data-testid等属性同时通过getBoundingClientRect判断它当前是否可见。把这些信息汇总成一个结构化JSON就是一份初版快照。我用过一个很粗糙的示例代码来跑这个流程async def collect_page_structure(page): return await page.evaluate(() { const results []; const selector button, input, a[href], select, [rolebutton], [roletextbox]; document.querySelectorAll(selector).forEach((el) { const style window.getComputedStyle(el); const rect el.getBoundingClientRect(); if (style.visibility hidden || style.display none) return; if (rect.width 0 || rect.height 0) return; results.push({ tag: el.tagName.toLowerCase(), text: (el.innerText || el.getAttribute(aria-label) || el.getAttribute(placeholder) || ).trim().slice(0, 50), id: el.id || , name: el.getAttribute(name) || , data_testid: el.getAttribute(data-testid) || , href: el.getAttribute(href) || , role: el.getAttribute(role) || , type: el.getAttribute(type) || , }); }); return JSON.stringify(results); })这段代码的重点隐藏元素排除、可见性判断、语义属性的提取。它虽然简单但已经能应对大多数测试页面的场景。如果你想将快照做得更丰富可以继续加入元素之间的层级关系用相对XPath表达、表单分组信息、以及页面路由状态。4.2 关键配置项模型选择、上下文组装、安全护栏平台跑起来之前有几个配置项必须提前想清楚否则后面处处被动。模型选择要区分“生成模式”和“修复模式”的差异。生成模式用能力强的模型允许较高单次生成成本因为它的输出决定了用例首轮通过率修复模式可以用速度更快、更便宜的模型因为修复是基于现场信息的局部补全不要求太强的创造力。实践下来这样的成本分配能在保证质量的同时大幅降低总费用。上下文组装有一个很反直觉的经验上下文不是越多越好而是越对齐越好。宁可把页面快照裁剪得短一些也要保证它与用户描述之间的逻辑对齐清晰。如果模型收到的是一大堆与当前操作无关的元素反而容易学偏。安全护栏方面我做了三层一是命令白名单——AI只能调用平台预设的交互API不能凭空执行任意JavaScript二是目标约束——生成的脚本只能在测试环境或沙箱环境里运行所有外呼请求打往Mock服务三是人工审批——凡是AI自动生成的修复补丁进正式用例库之前必须经过人工点击确认。这三层看起来繁琐但真的能避免AI在自动化测试框架里制造更大的事故。4.3 一个完整的生成示例从需求描述到可执行脚本拿一个最常见的场景举例——用户登录后跳转工作台并且能看到自己的用户名。在平台里输入一句话“验证已注册用户可以通过用户名和密码登录成功后跳转到工作台并显示用户名。”平台执行链路如下先启动一个浏览器会话打开被测系统登录页生成页面快照。快照里清楚地标记出账号输入框placeholder为“请输入用户名”、密码输入框、登录按钮以及登录区域的定位提示等。生成编排层把这个需求描述和快照一起组装成Prompt输出候选代码。我们用Playwright作为演示框架大致代码如下def test_login_success(page): page.goto(https://example.test/login) page.get_by_placeholder(请输入用户名).fill(test_user) page.get_by_placeholder(请输入密码).fill(test_pass) page.get_by_role(button, name登 录).click() page.wait_for_url(**/dashboard) expect(page.get_by_text(test_user)).to_be_visible()这段代码看起来很普通但背后有几个细节是AI生成时必须处理好的。第一模型没有直接用XPath或class而是结合快照里的placeholder和role来选择元素稳定性更好。第二等待条件用了wait_for_url而不是睡死时间这在生成策略里被明确强调过。第三断言验证了用户名可见这就避免了很多AI用例只跑到登录成功就结束的通病。当然实际第一次生成不太可能百分百完美。比如我见过模型把“登录按钮”误识别成页面上的另一个链接也见过它在断言阶段选择了一个不稳定的计数元素。这时候就进入人工微调阶段而平台要做的是把用户修改的结果作为反馈数据记录用来后续微调Prompt或构建Few-shot示例库。4.4 执行结果回流不追求一次生成而是快速纠错这一点是我最想强调的AI生成平台的竞争力不在于首次生成准确率而在于“一次失败后能多快修复”。如果首次生成准确率是60%传统做法是用户从零开始改脚本成本极高。但如果平台能自动识别错误并给出候选修复哪怕修复准确率只有50%循环两次后也能把准确率叠到90%以上。执行结果回流机制是关键中的关键。回流流程是这样的测试代码执行失败后平台同时采集四类信息——浏览器控制台报错、网络请求状态、DOM现场快照、以及失败步骤的截图。然后把它们组装成一个诊断包丢给修复模型。修复模型输出的补丁先在一个隔离环境里用一小部分回归集验证验证通过后再提交给用户确认。我在一次内部演示中观察到一个很有意思的案例一个按钮因为前端的状态机变化导致点击时机不对传统的人工排查要看控制台、看网络、看截图前后得花十几分钟而修复模型在拿到诊断包后几秒钟内判断出是“按钮仍在disabled状态”给出的建议是修改等待条件把“等待元素可见”改成“等待元素可点击”。这种诊断为测试人员省下的时间是非常可观的。5. 常见问题与排查技巧实录5.1 LLM“编造”元素幻觉问题怎么压AI生成脚本最常见的问题就是编造页面元素。模型在看了一段页面快照之后如果快照本身有遗漏它很可能“脑补”出一个看起来合理但不存在的输入框或按钮。这个问题在复杂页面里出现的频率相当高。排查办法是三层平台在生成前做快照完整性校验如果发现快照里可交互元素非常少就主动标记“页面加载可能不完整”引导用户刷新或等待。生成后做静态校验逐一检查生成代码中引用的元素是否都能在快照中找到对应项找不到就抛警告。执行时再做一次运行时校验元素定位失败时把期望定位器和页面实际存在的候选列出来辅助诊断。这个问题的根源不在模型本身而在于上下文的质量。页面快照粒度太粗、时机太早都容易诱发幻觉。实操中我建议把快照采集从“页面load后一次性抓取”改为“在关键交互前后各抓一次”并加上动态内容轮的等待这样能显著降低幻觉率。5.2 动态页面老是等不到元素UI自动化的痛动态列表、异步加载、组件懒加载要占一大半。AI生成脚本时天然会对“等待”的逻辑犯难——它不知道该等多久也不知道该等什么条件。平台的解决思路是让“等待策略”变成一个可配置的、随代码生成自动适配的模块。具体来说生成器会分析页面的交互类型并自动选择等待条件点击类操作等待目标元素可点击输入类操作等待输入框可编辑跳转类操作等待URL路由变化数据展示类操作等待关联API响应完成。这样AI不再是死板地sleep秒数而是生成更稳健的显式等待。另外一个很容易被忽视的点动态列表里新渲染出来的元素可能被浏览器标记为“视口外不渲染”导致Playwright拿到空节点。这里要把快照的可见性判断从“是否在视口内”改成“是否在DOM中且display可用”等滚动到对应区域后再触发一次重新提取。5.3 生成的脚本在浏览器里跑不动跑不动的原因大多数不是语法错误而是一些“隐性假设”没有对齐。最常见的有三种目标页面需要登录态但生成脚本里没有前置注入接口返回了Mock数据导致页面渲染异常浏览器被安全策略拦截了第三方请求元素加载不出来。这类问题的排查思路是建立“执行环境指纹”。每次执行前平台记录浏览器UserAgent、登录态、Mock开关、网络拦截规则、环境域名等把它和用例的依赖声明做比对。生成器在出码时也会根据依赖声明自动插入前置操作比如先调接口初始化登录态再打开页面。这个小设计极大减少了“脚本是对的但环境不对”的挫败感。5.4 平台自身怎么防滥用、防失控权限与隔离AI生成平台引入了新的风险生成了大量脚本如果不加管控很快就会形成一个新的“测试债沼泽”。我在平台上加了几个务实的手段。第一是生成配额管理每个项目每周能消耗的AI调用时长和Token量有上限防止个别人把平台的成本跑穿。第二是脚本入库双人复核AI生成的脚本必须经过负责人审核和指定成员抽查两关才能合入避免单人误操作投毒。第三是执行环境隔离所有AI生成的脚本默认跑在无头浏览器里禁止访问内网以外的地址并且强制超时阈值。这些手段不复杂但能帮平台在团队里建立起长期信任。6. 落地过程中的边界与下一步思考6.1 LLM在UI测试里能做什么、不适合做什么探索了大半年我对LLM在UI测试里的边界有了更清晰的认知。它能做好的是元素提取与描述、单步操作代码生成、业务流的组装、断言建议、失败诊断与定位器修复。这些事情本质上是“信息密集、规律明显、样例丰富”的工作非常适合模型学习。它不擅长的是需要结合复杂业务规则来判断的断言逻辑比如一笔订单是否符合某种审核策略、跨系统数据流的一致性校验比如前端展示的数据是否和多个后端服务的数据完全一致、以及需要理解产品设计意图的交互合理性评估比如这个按钮到底该不该出现。在这些场景里AI生成的建议只能作为提示不能替代人的判断。认清这条边界才能避免把平台做成一个“看着热闹但不敢信”的摆设。6.2 资产复用、数据管理、团队协作的配套工程最后想聊聊平台之外的东西。AI生成平台再顺手也只是技术域里的一环配套工程不到位照样落不了地。用例资产要复用必须建立一套语义级用例表示而不是让用例直接以代码形式存在库里。这样同一份用例既能生成Playwright代码也能生成Selenium代码将来还能生成一些低代码自动化平台可导入的格式。测试数据管理也要纳入平台——AI生成的脚本里涉及账号、密码、订单号都应该从统一的数据池里引用而不是在Prompt里临时捏一个。团队协作上平台需要支持“用例评审流”和“生成日志审计”让每个人都能看到哪些代码是AI写的、哪些是人工改的、改了什么避免质量责任边界模糊。我个人在实际操作中的体会是这类平台最大的价值不在“替代人写脚本”而在“把测试人员从重复劳动中释放出来让他们把时间花在设计更好的测试场景和判断系统质量上”。目前这个原型平台距离成熟还远但它让我和团队第一次感觉到UI自动化测试的维护成本也许真的有希望脱离“越做越多、越多越累”的螺旋。如果你也想做类似的事情我的建议很直接不要一上来就规划宏大平台先花两周时间把你的测试团队里最耗时的三种脚本类型找出来针对其中一种做最小闭环验证。跑通之后再去扩展大概率比直接铺开全面转型要稳妥得多。