现在 AI 已经很会 “生成” 了。
你可以让它生成测试用例、生成自动化脚本、补预期结果、改老用例,甚至把一批历史用例整理成新的测试资产。问题不再是 “能不能生成”,而是:它凭什么生成得准?
很多团队手里都有一堆 “古早用例”:当年靠人理解业务写出来,步骤省略、预期含糊、页面文案过期、前置条件写在测试人员脑子里。人执行时还能靠经验补齐,交给 AI 就会暴露问题。AI 看见 “点击发布”,并不知道要先进哪个页面、按钮在哪里、什么情况下按钮能点、点完之后弹窗该怎么处理。
如果没有一份完整、真实、可追溯的文档做输入,AI 生成出来的东西很容易变成 “看起来对”:
所以我越来越觉得,AI 测试生成的核心,不只是 prompt,也不只是模型能力,而是输入工程:我们能不能先把系统本身整理成一份机器能消费、也能被人审查的知识底座。
这篇文章分享的是其中一个具体案例:怎么从前端代码库里提取出一份「可交互元素清单」,让 AI 在生成新用例、完善旧用例、生成自动化脚本时,都有足够可靠的上下文。
前端只是切入口。背后的方法其实更通用:把代码里的真实行为,抽成结构化、可追溯、可持续更新的测试输入。换成后台接口,也可以用类似思路提取接口清单、参数约束、状态流转、错误码和调用前置条件。
一开始很容易把目标想窄:既然要做 UI 相关测试,那把页面上所有按钮、输入框、菜单项列出来,不就行了吗?
后来发现不够。
对 AI 来说,一个按钮本身没有太大价值。真正有价值的是围绕这个按钮的一整段上下文:
换句话说,我们要的不是一张「控件花名册」,而是一份可以指导 AI 理解产品行为的页面模型。
我现在会把它拆成 10 类信息:
| # | 信息 | 对 AI 生成有什么用 |
|---|---|---|
| 1 | 有哪些页面 | 划定被测范围 |
| 2 | 页面之间怎么跳 | 补全导航前置步骤 |
| 3 | 哪些区域由 SDK 或引擎渲染 | 标记需要继续深追的区域 |
| 4 | 每页藏着哪些弹窗、抽屉、子页 | 不漏隐性的 UI 单元 |
| 5 | 每个单元里有哪些可交互功能 | 找到真正的测试对象 |
| 6 | 元素真实显示什么文案 | 生成可靠定位器,也能更新旧用例里的过期文案 |
| 7 | 功能源码在哪里 | 追行为、写断言、做溯源 |
| 8 | 从入口怎么一步步到达控件 | 补全测试 setup 和老用例缺失步骤 |
| 9 | 点击前需要满足什么前提 | 避免误判失败 |
| 10 | 点击后的完整交互序列 | 生成用例主体、断言和自动化脚本 |
这里最容易被低估的是第 9 和第 10 项。
举个真实例子:信息发布页的「发布」按钮。它看起来只是一个按钮,但真正能点下去之前,需要满足好几个条件:名称不能错、发布策略要校验、内容要添加、设备要选择、网页型内容要解析成功;如果遇到播放冲突,还要先在冲突弹窗里点「继续发布」。
如果这些前置条件没提取出来,AI 不管是生成新用例、改老用例,还是写自动化脚本,都会变得很悬。它失败了,但你不知道是产品坏了、用例坏了,还是前置状态根本没搭好。
这件事能做,是因为前端代码并不是任意形状的。
不管是 Vue、React,还是其他现代前端框架,UI 交互最终都会落在一些相对稳定的表达方式上:
ref、reactive、useState、store;这些机制在框架里长得不一样,但本质很像。
Vue 里你会搜 @click、v-model、useDialog、router.push。React 里你会搜 onClick、useState、createPortal、navigate。锚点不同,追踪思路是同一套。
所以我现在更愿意把这件事理解成:
前端框架把 UI 交互压缩进了有限几种代码模式里。我们要做的,是把这些模式重新展开成测试能理解的路径。
当然,光知道模式还不够。动手之前还要先摸清项目本身:
这些项目级事实最好由脚本先探测出来。否则 LLM 拿着一堆搜索锚,也不知道该往哪些目录里搜。
我试过让一个 agent 一次性读完整个项目,然后直接吐出完整清单。效果很不稳定。
原因也很朴素:上下文不够、容易漏、还容易编。它可能读到了主页面,却没追到弹窗;追到了弹窗,又忘了菜单里的操作;为了把结果写完整,还会用一些看起来很合理但源码里没有的描述补上。
最后比较稳的方式,是把任务拆成几段,每段只解决一个问题,产物落盘,下一段继续消费。

每一步的职责尽量单一:
| 阶段 | 主要问题 |
|---|---|
| detect | 项目里有哪些页面?页面之间怎么跳?哪些区域不是普通页面源码直接渲染的? |
| deep_research | 每个页面里还藏着哪些弹窗、抽屉、面板、子页? |
| restoration | 每个 UI 单元里有哪些能点、能填、能选的功能?页面真实文案是什么?源码在哪? |
| link | 从入口怎么走到这个功能?点击前要满足哪些门槛? |
| interaction | 点击之后发生什么?弹出的二级操作有没有继续展开? |
这套拆法不是为了显得流程很完整,而是为了克服 LLM 的几个实际弱点:
拆开之后,每一步都可以用更小的上下文、更明确的验收脚本和更清晰的文件边界来约束它。
这三个词听起来有点像工程口号,但它们其实来自很具体的失败场景。
完整,是为了防漏。
漏掉一个弹窗里的「确定」按钮,后面的用例和脚本就很难覆盖它。漏掉一个下拉选项,生成出来的用例就只测了表面路径。
真实,是为了防编。
元素文案必须来自真实渲染文本,不能把 deviceName、publishStrategy、form.submit 这种字段名或翻译 key 当成页面文案。源码位置也必须真的存在,最好精确到 path:line。
可追溯,是为了方便人接管。
当测试生成器写出一条断言时,我们要能反查:这个按钮从哪个组件来的,前置条件从哪几行代码来的,点击后的弹窗是在哪里挂载的。如果只有一段自然语言总结,出问题时就很难修。
为此,每个阶段结束后都要过机器检查,再加人工抽审。
机器适合查这些:
人工更适合看这些:
我自己的经验是:不要指望 LLM 自检解决所有问题。LLM 自检能降错,但终判最好交给确定性脚本和人工抽样。
落到 Vue 项目里,搜索会围绕这些锚点展开。
| 要找的信息 | Vue 里常见的锚 |
|---|---|
| 页面和路由 |
routes: [、component: () => import、<router-view>
|
| 跳转关系 |
router.push、router.replace、<router-link>、菜单配置 |
| 可交互元素 |
@click、@change、v-model、@update:、defineEmits
|
| 真实文案 |
$t(、t('、label、placeholder、slot、locale 文件 |
| 弹窗和抽屉 |
useDialog、<Dialog>、Modal.confirm、h()、createApp、teleport
|
| 状态副作用 |
ref、reactive、Pinia/Vuex store、provide、inject
|
| 动态组件 |
<component :is>、动态 import()、组件 registry |
| 网络和配置 |
request、axios、接口定义、远程 JSON、window.*
|
实际追的时候,我会遵守几个顺序。
先从配置和入口开始,不要一上来钻进某个组件。路由表、菜单配置、app.use()、SDK 注册顺序,能先帮我们建立一张项目地图。
然后按语法锚搜索,不按感觉猜。看到 @click="handleSure",就追 handleSure;看到 useDialog(SomePanel),就知道这个面板不在当前 template 里,要顺着命令式挂载继续追。
遇到 i18n,必须追到 locale。$t('content.publish') 不是页面文案,「发布」才是。
遇到副作用跨组件,不能只看 import。store、provide/inject、事件总线、动态组件、配置注册、iframe、postMessage,都可能让一个点击影响到另一个地方。
最后,在 interaction 阶段一定要穷举到底。不能写「点击后打开设置面板」就结束,而要继续读设置面板源码,把里面每个可操作项展开出来。对自动化测试来说,「打开面板」只是开始,不是结束。
还是用信息发布页的「发布」按钮做例子。
第一步,靠 @click 找到入口。比如模板里有 @click="handleSure",那它就是一个功能点。
第二步,读 handleSure。这里不能只看函数名,要看它具体做了哪些事:校验名称、校验发布策略、检查内容、检查设备、处理网页解析状态、判断播放冲突。
第三步,顺着命令式挂载继续追。如果冲突时会 useDialog(...) 打开确认弹窗,那弹窗里的「继续发布」和「取消」也必须进入交互序列。
第四步,把前置条件写清楚。比如:
第五步,把点击后的结果展开。校验失败会出现什么提示,冲突弹窗里点「继续发布」会继续走哪条链路,成功后页面是跳转、刷新还是 toast,这些都应该进入最终的交互描述。
这时,「发布」按钮才从一个孤零零的 DOM 元素,变成了一段机器可以执行、也可以被人审查的测试路径。
目前这类可交互元素清单主要有几种消费方式。
第一种是生成新用例。
过去让 AI 生成用例,常见输入是需求文档、页面截图、测试人员补充的一段说明。这些当然有用,但经常不够细。元素清单补上的,是代码里真实存在的交互边界:有哪些入口、哪些控件、哪些弹窗、哪些前置门闸、哪些异常分支。AI 再生成用例时,就不只是 “按经验猜场景”,而是能围绕真实行为枚举路径。
第二种是清洗和更新老用例。
很多人工用例的问题不是业务错,而是写得太省略:缺前置步骤、缺预期结果、控件描述含糊。拿元素清单对照之后,可以把「点那个按钮」补成真实文案,把缺失的导航链补上,把点击前置条件和点击后的断言补完整。
这对历史用例尤其重要。老用例经常停留在旧页面文案、旧入口、旧交互上。只靠人肉维护,很容易越积越乱;让 AI 直接改,又容易把错的东西改得更顺口。元素清单提供了一把尺子:哪些步骤还对,哪些文案过期,哪些前置条件缺了,哪些预期结果已经和代码不一致。
第三种是生成 UI 自动化脚本。
如果走 Computer Use 模式,可以把语义化路径交给 LLM,让它在真实浏览器里操作。它看到的不是坐标脚本,而是类似「进入设置菜单,在通用分区点击主题」这样的步骤。布局变了,只要语义没变,就不至于全盘崩掉。
如果走 Playwright 生成,清单里的真实文案和交互序列可以直接变成定位器、步骤和断言。它不再只是一个空架子,而是有页面依据、有源码依据的 E2E 用例草稿。
第四种是做测试知识的持续更新。
代码在变,测试资产也应该跟着变。可交互元素清单如果能持续从代码里重新提取,就可以反过来发现测试资产的漂移:页面新增了功能但没有用例,老用例引用了已经不存在的文案,某个弹窗新增了确认步骤但自动化脚本还停在旧路径上。
这时 AI 的角色就不只是 “帮我写一条用例”,而是 “帮我维护一份持续对齐代码的测试知识库”。
我现在越来越觉得,AI 生成测试最难的部分不一定是 “怎么让它写”,而是 “拿什么喂给它”。
前端代码里其实已经有答案:页面、路由、文案、状态、弹窗、校验、跳转、网络请求,都在代码里。只是它们散落在模板、组件、store、配置、locale 和 SDK 之间。
把这些信息重新组织起来,AI 生成用例、完善旧用例、生成自动化脚本,才有了比较稳的输入。
前端代码提取只是一个案例。换成后台接口,也可以做类似的事:从路由、controller、service、schema、数据库约束和错误码里,提取接口能力、参数规则、状态流转和异常分支。领域不同,抽取对象不同,但目标是一样的:把代码里真实存在的行为,变成 AI 可以消费的测试上下文。
这也是这套方法论最朴素的出发点:先别急着让机器生成,先让机器知道系统到底是什么。