尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

大模型驱动无代码测试:从自然语言到可执行脚本

大模型驱动无代码测试:从自然语言到可执行脚本 我最早接触“无代码测试”这个词时第一反应其实是排斥。原因很简单市面上号称无代码的方案绝大多数只是把“写代码”换成了“拖控件”和“录制回放”脚本一换环境、一改选择器就碎成一地。真正让我转变态度的是去年在一个内部工具项目里尝试引入大模型把业务同学写的自然语言用例直接转成可执行测试脚本。第一次跑通的时候业务同事看我的眼神都不太一样了。这篇文章不是来吹“AI取代测试工程师”的而是想把我在落地过程中拆过的原理、写过的提示词模板、踩过的坑一次性讲清楚。主题就一个AI把自然语言变成测试脚本之后无代码测试到底该怎么设计、怎么实现、怎么避免翻车。内容尽量从零开始适合正在搞自动化测试平台、想用大模型提升测试效率的团队参考。1. 无代码测试的进化逻辑AI解决的是“语言鸿沟”1.1 传统无代码测试为什么名不副实先聊聊过去十年常见的三种无代码测试方案它们的局限你都遇到过。第一种是录制回放。工具把你的鼠标键盘操作录下来生成脚本。听起来很美好可网页稍微异步刷新一下回放就找不到元素了按钮改了文案选择器失效登录状态一变整个流程直接从第一步崩到结尾。录制回放适合回归验证但维护成本高到你不如自己写代码。第二种是图形化编排。把“打开页面”“点击按钮”“输入文本”“断言文本”这些积木拖一拖、连一连。这类工具对纯业务人员有一定友好度可一旦你要做参数化、条件分支、循环读取测试数据你就得回到代码编辑器里敲表达式。它只是把代码藏在了积木后面并没有真正理解你想要的逻辑。第三种是BDD框架典型如Cucumber配Gherkin语法。Given When Then确实把用例写得像人话但运行这些自然语言用例仍然需要测试工程师为每个步骤编写对应的Step Definition。说穿了自然语言在这里只是一种“标签”背后的逻辑还是要手写。所以传统无代码测试的真实状态是它降低了“写第一版脚本”的门槛却没有降低“维护脚本”的门槛更没有解决“把人类意图变成测试动作”的核心问题。1.2 大模型真正的价值从“模板匹配”升级到“意图理解”大模型介入之后事情的本质发生了变化。它不是把文本套进一个固定模板而是做语义理解。我举个例子。业务同学写“点一下登录”运维同学写“按下登录按钮”产品文档里写“提交登录表单”。在传统规则引擎里你要写三套关键词去匹配在LLM面前这些都会被统一理解为同一个意图click作用于登录按钮。再比如“用户名输入admin密码填123456”。模型知道“用户名”“账号”“登录名”指同一个输入框“填”“输入”“录入”都是fill动作。它还能结合页面上下文推断元素定位策略。这种跨表达方式的归一化能力是之前的“无代码”工具完全不具备的。这就是我理解的进化方向无代码测试不再追求“不用写代码”而是追求“用自然语言描述测试意图系统直接生成可靠脚本”。生成之后你甚至不需要读代码只要确认“这段脚本做的事和我描述的意图一致”就行。1.3 一个可落地的整体架构在动手之前先把流程画出来不用图表你就想象一个流水线业务/测试人员用自然语言描述一条测试场景。系统将这段描述拼入设计好的提示词模板发给大模型。大模型不直接产出Python代码而是产出一份结构化的动作清单JSON格式。一个本地的渲染器把这串JSON翻译成目标测试框架的脚本。测试运行器执行脚本产出结果失败信息再回传给模型或直接展示给使用者。为什么不让大模型直接生成完整代码因为我试过第一版就是这么做的后来发现几类问题模型偶尔会直接编造不存在的API方法代码里混入Markdown代码块标记缩进错误导致整文件语法失败。让模型输出结构化JSON再由本地渲染器生成代码既保留了模型的语义理解能力又把语法风险牢牢控制在自己手里。2. 把一句自然语言拆成“测试能懂的动作”2.1 一句话里的四个信息层次无论业务同学怎么描述一条可执行的测试场景几乎都包含四类信息操作对象页面登录页、订单列表、元素用户名输入框、提交按钮、商品卡片。动作打开、输入、点击、选择、勾选、滚动、等待。数据具体文本、数值、上传文件路径有时是变量如“随机生成手机号”。断言期望结果例如“页面显示欢迎回来”“按钮变为禁用状态”。举个例子原始描述是“打开登录页输入admin账号和密码123456点击登录应该看到欢迎回来的提示。”LLM解析出来后动作清单大约是访问登录页 →goto在用户名字段输入“admin” →fill在密码字段输入“123456” →fill点击登录按钮 →click断言页面出现文本“欢迎回来” →expect_visible / contains_text这个解析过程并不难理解关键在于提示词要引导模型把“账号”“密码”“登录按钮”拆成结构化字段而不是糊成一锅粥。2.2 动作映射从“人话动词”到“框架方法”为了让模型输出稳定我通常会给它一个动作白名单。白名单不是限制模型发挥而是防止模型生成五花八门的自定义方法。以Playwright为例我常用的映射关系如下自然语言意图动作类型Playwright/Python 示例打开/访问/跳转gotopage.goto(url)输入/填写/录入fillpage.locator(...).fill(value)点击/按下/提交clickpage.locator(...).click()勾选/取消勾选check/uncheckpage.locator(...).check()下拉选择select_optionpage.locator(...).select_option(value)鼠标悬停hoverpage.locator(...).hover()等待元素出现wait_visibleexpect(...).to_be_visible()校验文本expect_textexpect(...).to_contain_text(value)白名单的作用是双重的一方面降低模型幻觉概率另一方面让渲染器代码变得极其简单——一个字典查表就能完成动作到代码的转换。2.3 元素定位模型不知道选择器怎么办这一节最容易踩坑。很多第一次做AI测试生成的人会问模型连我们的页面DOM都没见过它怎么知道“登录按钮”对应哪个选择器答案是不管它怎么知道你要么给它上下文要么让定位策略后置。我在项目里用的是“混合定位”方案首先告诉模型生成动作时优先使用>你是一名资深测试开发工程师。你的任务是把用户提供的自然语言测试场景 转换成结构化动作清单用于生成 Playwright 测试脚本。 规则 1. 只输出 JSON不要输出任何解释、Markdown 标记或代码块。 2. 动作类型只能从以下列表选择 goto, fill, click, check, uncheck, select_option, hover, wait_visible, expect_text, expect_value, scroll 3. 元素定位优先级data-testid aria-label placeholder 元素文本。 4. 如果描述存在歧义在 action 里加一个字段 confidence值为 low 并在备注字段 note 里说明原因。 5. 如果描述中没有提到页面 URLgoto 的 url 字段用 null。 6. 输入数据里包含“随机”“生成”等词时value 字段写一个含占位符 的模板变量例如 {{random_phone}}。 7. 每条自然语言最多生成一个场景不要自动合并多个场景。 请严格按此格式输出 { scene: 场景名称, actions: [ {action: 动作类型, locator: 元素标识, value: 值, note: } ], assertions: [ {assert_type: expect_text, locator: 元素标识, value: 期望文本} ] }你也许注意到了我特意把断言单独从动作里剥离出来而不是统一混在actions里。原因是渲染的时候动作会写成顺序执行语句而断言在Playwright里通常用expect(...)表达两者结构不同分开更利于渲染器处理。3.2 渲染器把JSON变成可执行脚本JSON解决的是稳定性问题但真正要跑起来还得靠渲染器。这里是一段简化版的渲染核心逻辑各位可以根据自己的框架改。ACTION_TEMPLATES { goto: page.goto({url}), fill: page.locator({locator}).fill({value}), click: page.locator({locator}).click(), check: page.locator({locator}).check(), uncheck: page.locator({locator}).uncheck(), select_option: page.locator({locator}).select_option({value}), hover: page.locator({locator}).hover(), wait_visible: page.locator({locator}).wait_for(statevisible), scroll: page.mouse.wheel(0, {value}), } ASSERT_TEMPLATES { expect_text: expect(page.locator({locator})).to_contain_text({value}), expect_visible: expect(page.locator({locator})).to_be_visible(), } def render_to_script(json_data) - str: header [ from playwright.sync_api import expect, , def test_auto_generated(page):, ] body [] for item in json_data[actions]: tpl ACTION_TEMPLATES[item[action]] if item[action] goto: body.append(tpl.format(urlitem[url])) else: body.append(tpl.format(locatoritem[locator], valueitem.get(value, ))) for item in json_data[assertions]: tpl ASSERT_TEMPLATES[item[assert_type]] body.append(tpl.format(locatoritem[locator], valueitem.get(value, ))) return \n.join(header body)这样做的最大好处是无论模型怎么改输出最终落到磁盘上的Python代码永远符合规范。就算模型偶发多了一个没见过的动作类型渲染器也能直接抛错而不是生成一段跑不起来的脏代码。3.3 一个完整演示从自然语言到可执行脚本下面我用一个实际跑通过的小例子展示整个过程。原始输入“登录页面输入用户名admin密码admin123点击登录验证页面显示欢迎回来。”模型输出的JSON过滤掉低置信度字段后{ scene: 用户登录成功, actions: [ {action: goto, url: https://example.com/login}, {action: fill, locator: [data-testidusername], value: admin}, {action: fill, locator: [data-testidpassword], value: admin123}, {action: click, locator: [data-testidlogin-btn]} ], assertions: [ {assert_type: expect_text, locator: body, value: 欢迎回来} ] }渲染器生成的脚本from playwright.sync_api import expect def test_auto_generated(page): page.goto(https://example.com/login) page.locator([data-testidusername]).fill(admin) page.locator([data-testidpassword]).fill(admin123) page.locator([data-testidlogin-btn]).click() expect(page.locator(body)).to_contain_text(欢迎回来)这条脚本直接在pytest环境下执行通过了。整个过程里业务同学从头到尾没有看过一行代码他只需要确认两件事我描述的场景有没有被理解错以及最终执行成功后截图里的结果对不对。4. 落地到真实项目工具选型与框架打通4.1 为什么最终选了Playwright而不是Selenium自然语言生成脚本对底层自动化框架的要求比传统自动化更高要能方便地生成元素定位、要容忍异步渲染、要在断言失败时给出有效信息。Selenium很成熟但定位策略繁琐等待机制要自己写很多轮询Cypress对多标签页和跨域支持一般Playwright的优势在于自动等待、多定位器策略、内置expect断言重试机制并且API设计非常简洁非常适合作为LLM生成目标。所以我的结论是如果你从零开始直接选Playwright。如果团队已经用Cypress或Selenium沉淀了大量资产那就保留你现有的只把AI生成层的动作白名单改成对应框架的API。4.2 先出JSON再出代码这句我再强调一次在架构上我刻意把“模型输出”和“最终脚本”分成两层。这一设计的收益在我维护到第三个月时体现得特别明显大模型更新版本后同一段自然语言会生成略微不同的JSON但渲染层完全不受影响因为生产代码只依赖固定的动作类型白名单。如果你跳过JSON直接生成代码那要面对的就不是“JSON字段偶尔缺失”这种小事而是“模型某次把page.locator写成了page.locatror”这种直接挂掉的问题。JSON层等于加了一个稳定性的缓冲垫。4.3 与pytest和CI的集成方式生成出来的脚本不是一次性的。为了让它们能进入日常回归我建议把整个流程串进CI自然语言用例维护在Markdown或YAML文件里测试人员负责维护文字。CI定时任务把文字批量发给模型得到JSON。渲染器将JSON转成Python测试文件提交到测试仓库。pytest执行这些文件失败信息自动回传。有一个细节如果场景涉及测试数据不要写死在自然语言里。自然语言里写“输入一个已注册手机号”渲染后的脚本里就用{{registered_phone}}占位pytest用fixture在运行时注入数据。这样做的好处是同一个场景可以跑不同环境、不同账号。5. 实战中踩过的坑自然语言生成脚本的典型问题5.1 元素定位失败的根源居然是页面上下文缺失项目刚上线那两周生成脚本的失败率大概有四成绝大多数都是定位失败。业务同学写“点击订单详情”模型给了一个locator: text订单详情但页面上一共有五个“订单详情”入口渲染后执行Playwright每次都会因为多元素匹配弹错。后来我调整了策略不再完全依赖模型生成具体选择器而是给每个页面挂一份轻量的语义映射。比如已知当前页面有“订单列表”“订单详情”“筛选条件”这些区块就把这些区块的># Generated by LLM NL2Test # Source: docs/cases/login-success.md这两行注释的价值在出问题时会体现出来你能快速定位到是哪一条自然语言描述生成了这个脚本也能直接去改那段描述然后再生成一次而不是面向脚本本身修修补补。6.4 团队工作流建议测试人员管“话”工具管“码”最后想分享的是这几个月沉淀下来的团队协作方式。真正用得舒服的模式其实不是让测试开发去写提示词、去调API而是业务测试人员写需求场景内容覆盖操作步骤和预期结果。一个负责工具链的同事维护提示词模板、渲染器、执行环境。场景文字入库后由CI自动生成脚本并跑回归。失败场景由测试开发看一眼报错决定是修脚本还是补充自然语言描述里的上下文。这套流程跑顺后团队里不需要人人都懂AI原理测试人员只需要知道一件事把场景写清楚机器会还你一版能跑的脚本写得模糊或者缺了上下文机器还给你的就是一堆待补的洞。我在实际项目中最大的体会是AI把自然语言变成测试脚本真正改变的不是“写脚本”这个动作而是把“测试设计”和“测试实现”彻底分离了。自然语言用例本来就该属于业务人员脚本实现本来就应该可以自动生成。只是以前技术不成熟硬要让人迁就工具现在总算轮到工具迁就人了。如果你也打算在自己的项目里试记住我这句话别一开始就追求全量自动化生成先拿登录、注册、核心主流程这三条用例跑通再慢慢铺开。从小处验证比什么都强。
返回列表