AI生成测试脚本的鲁棒性工程实践:从老化测试到图像水印评测
这两年我在团队里最常听到的一句话就是用例可以写脚本真的不想写了。尤其是设备老化测试、图像水印评测这类需要长时间跑、反复改参数的场景一个脚本从手写到稳定少说一两天多了能磨一周。AI生成测试脚本这件事我最初是抱着“能用但不可靠”的心态去试的结果跑通一个完整闭环之后我发现真正值钱的地方不是“让AI写脚本”而是怎么让AI写的脚本在复杂、异常、多变的环境里依然稳定也就是标题里那个词——鲁棒性。这篇内容不聊概念直接讲我踩过的坑和沉淀下来的方法。我会把从零构建一个AI辅助生成测试脚本系统的完整过程拆开需求设计、模型选型、Prompt约定、用例与断言生成、老化测试和图像水印评测这两个强鲁棒性场景的落地、最后是执行过程中的问题排查。无论你是自动化测试工程师、测试架构师还是刚接触LLM的研发都能在这里找到可以直接拿去用的方案。1. 项目概述为什么需要AI生成测试脚本1.1 测试脚本自动化的痛点和AI的切入点传统自动化测试脚本的维护成本其实是很多团队不愿意正视的一笔隐形成本。业务接口一变一百条用例里可能一半都飘红逐条改断言逻辑能改到怀疑人生。而AI生成测试脚本的切入点恰恰就是这里把“人和接口文档之间的翻译工作”交给大模型让人把精力花在更重要的场景设计上。我最初的目标很朴素让大模型根据接口定义和业务说明直接产出能跑的pytest脚本。但这个目标很快就遇到了一个现实问题AI生成的脚本十次里有六七次能运行可真正敢让它直接进流水线的一次都没有。原因不外乎三点生成的用例覆盖面太随机、断言太弱、异常处理约等于没有。这也直接导致我把关注点从“生成”转移到了“鲁棒性”。给团队做分享的时候我常用一个类比手写测试脚本像是自己下厨每一道工序心里有数让AI生成脚本像是点外卖你拿到的是成品但不确定食材新不新鲜、有没有放你忌口的调料。所以整个系统的核心不是“点外卖”而是建立一个“厨房标准”——从食材需求抽取、配料Prompt设计、烹饪脚本生成到品控静态检查与试运行全部标准化。1.2 系统的最终能力与适用范围经过一段时间迭代我搭出来的这套AI生成测试脚本系统具备四个能力需求到用例的自动映射、可定制断言的脚本生成、多轮反馈修复、以及面向长时间运行的鲁棒性加固。它不是一个简单的“输入中文输出代码”的玩具而是一个带有校验闭环的工程框架。适用场景上我重点在两个方向做了验证。一个是接口层面的功能测试和性能回归脚本可以稳定生成并接入CI。另一个是强鲁棒性场景比如设备老化测试的全自动执行以及图像水印鲁棒性攻击中的弯曲变换、噪声注入等测试这几个场景对脚本稳定性的要求极高也最能体现“生成框架”和“堆代码”的本质区别。如果你只是想让AI写一段一次性脚本那闭眼用就行如果你要让它持续产出可维护、防异常、结果可解释的测试脚本那这篇就能帮到你。2. 整体设计从需求到脚本的工程化路径2.1 以鲁棒性为核心的系统架构拆解我在设计这个系统时没有把“AI生成”当成一个孤立模块而是把它放进了整个测试链路里。系统最终分成了五层LLM适配层负责统一封装不同大模型的调用包括本地部署模型和云端API屏蔽上下文窗口和返回格式的差异场景解析层把用户输入的需求文本、接口文档、历史用例进行结构化解析输出测试场景清单脚本生成层基于场景清单调用LLM生成可执行的测试脚本同时强制加入超时、重试和异常兜底逻辑静态检查与试运行层用AST解析和pytest dry-run验证语法再在小样本上试运行不能通过的脚本直接打回重生成结果回灌与鲁棒性评估层收集执行日志、失败原因、覆盖率数据反馈给LLM做下一轮优化同时按统计口径输出鲁棒性评分。这五层里最容易被忽略的是第一层和最后一层。LLM适配层如果做不好换一个模型就要改一套Prompt鲁棒性评估层如果缺失你根本不知道AI生成的脚本到底有多可靠只能凭感觉。我自己的经验是不要把“让AI写脚本”当成凭空生成而是把它当作一个持续迭代的生产线。每一轮执行结果都是下一轮的输入模型见过的失败越多它后续生成的脚本就越接近你的预期。2.2 为什么选择“生成-校验-回灌”而不是直接让AI写脚本很多人问过我直接用ChatGPT或者别的AI工具把脚本生成出来粘到项目里跑不就行了确实可以但那样生成的是“一次性脚本”换个环境、换份数据基本就废了。我选择“生成-校验-回灌”这个闭环核心考量是AI模型本质上是一个概率系统它给出的代码只是“看起来合理”的解而不是“经过验证正确”的解。如果你跳过校验就等于把不确定性直接引进了测试资产里。具体流程是这样LLM生成脚本后先用py_compile做语法检查再用pytest --collect-only验证用例能被收集接着在预先准备的一组极小样本上试跑。试跑通过后把覆盖率、通过率、失败原因三类数据回传给LLM让它针对性地修复。这个循环走三轮左右脚本的稳定性会有一个肉眼可见的提升。有一次我调一个登录接口的测试脚本第一轮AI生成的断言直接断言了token字段但没做token为空的判断试运行时接口返回异常数据脚本直接报了KeyError。回灌之后第二轮AI给出了防御性写法用get()加默认值整个脚本瞬间皮实了很多。这就是闭环的价值。2.3 Prompt工程让大模型输出可复用脚本的关键约定Prompt是这套系统里最容易被低估的环节。最开始我给的Prompt很随意比如“请生成一个登录接口的测试脚本”结果AI给的脚本五花八门有的用了requests有的用了httpx有的还自定义了类。这类脚本没法统一维护更没法批量治理。后来我总结了一套结构化Prompt的写法核心是五个固定模块角色定义你是一名资深测试开发工程师熟悉pytest和requests项目约束必须使用pytest框架夹具命名统一为setup_data断言必须是显式且可解释的输入信息接口文档、字段说明、状态码约定、已知边界值输出格式只输出Python代码不要解释并用# [用例名称]注释标注每一条用例负面约束不要生成数据库连接相关代码不要使用不存在的第三方库不要吞掉异常。这套模板看起来简单但实操下来效果非常明显。团队里其他同事拿着同一套模板去问不同的大模型产出的脚本结构基本一致后续审计和维护的难度低了很多。3. 核心环节实现AI辅助生成测试用例与断言3.1 业务需求到测试场景的映射让AI生成脚本的第一步其实是生成“测什么”。这一步要做到位需要把天然语言的需求转成结构化的场景描述。我常用的做法是让LLM先输出一个YAML形式的测试场景清单然后再基于这个清单生成具体代码。举个例子如果需求是“用户可以通过手机号和验证码登录”那么一个合格的场景清单至少包含正确手机号与正确验证码登录成功、错误验证码登录失败、手机号未注册、验证码过期、短信发送频率超限、连续失败锁定等。AI在自然语言理解方面确实有优势如果你把历史用例喂给它它还能模仿团队的习惯来补充场景。但AI生成的场景有时候会天马行空比如生成一个“手机号包含emoji”这种现实中根本不存在的case。所以我加了人工复核这一步。场景清单生成后会有测试人员做一次增量审查把不符合业务逻辑的剔除把遗漏的关键路径补上。这样做既保留了AI的效率又没有把判断力完全交给概率。3.2 边界条件与异常路径AI最擅长也最需要约束的地方边界条件是AI生成测试用例里最有价值的产出也是最需要约束的产出。我观察到AI在主动生成边界值方面表现远超普通人尤其是对数字范围、字符串长度、空值、超大值这些常见的输入边界它几乎不用提示就会自动覆盖。但问题也随之而来AI会过度生成。有一次它给一个分页接口生成了page-1、page0、page1、page99999看起来覆盖很全但实际上接口对page0和page1的处理逻辑相同这就是无效冗余。约束的办法是在Prompt里加一句话每个边界用例必须注明其针对的边界条件若两个用例触发相同的代码路径请合并。异常路径方面我比较看重超时、下游服务熔断、数据库不可用这三类。AI如果不了解系统的架构很难主动覆盖这些场景我的做法是把常见异常注入模板写进Prompt里明确要求LLM在每条接口测试用例旁边生成一个对应的异常分支测试。3.3 断言设计让AI生成“可证明通过/失败”的校验逻辑断言是测试脚本的灵魂而AI生成的断言恰恰是最容易“假通过”的地方。很多AI生成的断言只有assert response.status_code 200这等于没测。如果接口返回一个{code:500, msg:error}但HTTP状态码是200这样的断言就会漏报。我的经验是强制要求AI用“多层级断言”模式def test_login_success(): resp client.post(/api/login, json{phone: 13800138000, code: 123456}) assert resp.status_code 200, fHTTP状态码异常: {resp.status_code} data resp.json() assert data[code] 0, f业务码异常: {data[code]} assert data[data][token], token不能为空 assert data[data][expires_in] 0, token过期时间必须为正数这种断言风格的好处是每一层失败都能直接定位到问题类型而不是笼统的“接口挂了”。为了让AI稳定输出这种风格我会在Prompt里给一个类似的few-shot示例并明确要求禁止只断言状态码必须增加业务码和关键字段断言。对于结果校验更复杂的场景比如老化测试里判断设备是否过热重启或者水印测试里判断提取的图片与水印原始图是否一致则需要额外引入统计指标。AI可以帮你生成计算正确率、误码率的辅助函数但阈值的设定必须由人来拍板。部分场景我甚至建议在代码里写死阈值并用注释说明来源防止后续被随意修改。4. 鲁棒性专项老化测试与图像水印场景实测4.1 设备老化测试7x24小时全自动执行的项目化落地先聊设备老化测试这个场景。老化测试要求设备在高温高湿等恶劣条件下连续运行数天脚本需要在后台7x24小时循环执行期间不能跳出异常弹窗导致崩溃不能因为单次IO超时造成整轮丢失还必须实时记录日志、截图、环境数据。这种需求用人工手写脚本很繁琐但AI生成的核心框架可以直接复用。我给这套老化测试脚本定下的核心逻辑是三层兜底最内层是单次操作级别的异常捕获和重试中间层是任务级别的断点续跑最外层是看门狗机制超过一定时间没有心跳就重启整个测试进程。AI生成初版时就已经包含了前两层最外层的看门狗是我在试运行后补充的因为实测发现某型号设备偶发系统挂起Python进程既不退出也不抛出异常普通的重试根本解决不了。老化测试脚本里还有一个很容易忽略的点时间戳和日志。AI会自然地生成logging.info(start test)这种日志但不会主动包含设备温度、系统版本这些关键上下文信息。我在Prompt里加了一条硬性要求所有日志输出必须携带本轮循环序号、累计运行秒数和关键环境变量。这样一旦凌晨三点脚本出问题第二天看日志能直接定位是第几轮、设备什么状态而不是从头复盘。4.2 图像水印鲁棒性攻击中的抗干扰脚本设计图像水印的鲁棒性测试跟老化测试完全是另一个画风。水印鲁棒性测试里经常需要模拟各种攻击或误操作看水印还能不能被正确提取。热门搜索里那个“bending”就是图像弯曲变换属于一种典型的几何攻击。类似变换还有旋转、缩放、平移、裁剪、噪声叠加、JPEG压缩等。这部分测试脚本要求非常高每张测试图片要经过多种攻击参数组合每种攻击后都要计算水印提取正确率。如果用传统方式手写攻击函数和评测函数混在一起代码很快会膨胀到没法维护。AI生成方案里我让它按“攻击函数注册表”的架构来组织代码每种攻击单独一个函数统一接收图像数组和参数返回攻击后的图像。这样后续加新攻击只需要增加一个函数不需要改动评测主流程。实际脚本长这样def bending_attack(img, strength0.2): from scipy.ndimage import map_coordinates h, w img.shape[:2] y, x np.meshgrid(np.arange(h), np.arange(w), indexingij) x_new x strength * np.sin(2 * np.pi * y / 50) y_new y strength * np.cos(2 * np.pi * x / 50) return map_coordinates(img, [y_new, x_new], order1).reshape(h, w, -1) if img.ndim 3 else map_coordinates(img, [y_new, x_new], order1)AI生成这个函数的初版几乎一次通过因为它对scipy.ndimage.map_coordinates这类常用API很熟悉。倒是后续的评测脚本让我费了不少心思水印提取的结果和原始水印做逐比特比较后还要统计平均正确率、最差情况正确率、以及特定攻击参数下的退化曲线。这些统计逻辑AI能写但必须人工确认计算公式。4.3 小波变换与LSB的对比测试AI如何辅助搭建评测矩阵再说热搜词里那个“小波变换与LSB性能比较”。LSB最低有效位是一种简单的空域水印算法小波变换则是在频域里嵌入水印。做这个对比实验核心不是“哪个算法更好的结论”而是一套公平的评测流程同一批测试图像、同样的攻击参数范围、同样的提取判定标准。我在这个任务里让AI承担的不是写算法而是生成评测矩阵。输入是图像文件夹、两种水印算法的嵌入和提取接口输出是一个CSV表格记录每张图在六种攻击类型、三组强度下的提取正确率和峰值信噪比。AI生成表格统计代码的效率非常高我只需要在Prompt里定义清楚列名和计算口径剩下的数据透视它自己就处理好了。对比结果其实很有参考价值。在我用的测试集上小波变换对JPEG压缩和低通滤波的鲁棒性明显更强而LSB算法在无攻击情况下的不可见性更好遇到bending这类几何攻击两者都出现明显下降但小波变换的退化曲线更平滑。用AI生成的脚本来做这个评测最大的好处是数据可复现任何人拿到测试集和脚本都能跑出同样格式的报告。5. 实操全过程从零构建并调优第一个AI生成测试脚本5.1 环境准备与模型接入如果你是从零开始复现这套系统环境准备其实不复杂。我用的主力组合是Python 3.10 pytest requests openai SDK模型既可以是云端API也可以是本地部署的开源模型。本地部署的好处是数据不出内网适合公司内部接口测试坏处是需要一块像样的GPU显存不够的话生成速度会很煎熬。环境准备阶段有一个建议在pip安装后就立即锁定依赖版本。AI生成的脚本里经常会用到requests、pytest、numpy这类常见库如果你的环境里这些库版本过旧生成脚本就算逻辑正确也可能因为API差异跑不起来。可以先在项目里维护一个requirements.txt把基础库版本固定好再让AI在此基础上生成。接入部分我做了一个简单的模型调用封装from openai import OpenAI client OpenAI(base_urlhttp://localhost:8000/v1, api_keyEMPTY) def generate_script(prompt: str) - str: resp client.chat.completions.create( modelqwen2.5-coder:14b, messages[{role: system, content: SYSTEM_PROMPT}, {role: user, content: prompt}], temperature0.2, ) return resp.choices[0].message.content这里有个细节temperature我固定设在0.2不设高。代码生成这类任务稳定性远比创造性重要temperature太高会导致同样的Prompt每次生成的结构差异很大增加后续维护成本。5.2 首次生成Prompt输入与实际输出分析为了演示我把完整流程走一遍。假设要生成一个图书搜索接口的测试脚本接口文档里写的入参是keyword、page、size出参是total和items列表。我的Prompt会写成你是资深测试开发工程师。使用pytest和requests框架。 接口信息GET /api/books/search入参keyword必填page从1开始size默认20最大100。 请生成测试脚本覆盖正常搜索、空结果、关键字为空、page小于1、size超过100、超时场景。 每条用例必须有独立的函数名断言包含HTTP状态码、业务码、total字段类型和items列表长度。 只输出Python代码。AI第一次生成的脚本大体结构是能用的正常搜索和空结果两条用例写的没什么问题但至少有三个明显缺陷page0的用例只断言了状态码没有断言是否返回400还是自动纠正超时场景用了requests.get默认超时根本不触发空关键字那条用例的断言写的是assert data[total] 0但接口文档里明确说空关键字应返回参数校验错误。这些缺陷如果直接上流水线就是明显的漏测。这一轮分析的价值在于它印证了开头说的观点AI生成脚本初期必须有人工审查但它的产出已经能覆盖八成的工作量你要做的是补齐那两成业务细节而不是从零开始写。5.3 循环调优让脚本从能跑到稳定跑第一轮生成后我把失败信息、人工审查意见、接口文档补充片段一起回灌给AI要求它重新生成。第二轮AI通常会修正空关键字的断言补上timeout参数并把page0的用例改成预期异常。第三轮主要解决风格问题比如把response.json()的重复调用抽成局部变量把公共的请求逻辑放到夹具里。三轮之后这个脚本基本达到了可以提交MR的水平。我统计过不同任务需要的循环轮次纯接口功能测试平均两轮半带复杂断言的业务测试平均四轮像图像水印这种既有计算又有统计的任务往往需要五轮以上。最好用的方式不是让AI无限迭代而是每轮都给出明确的失败信息AI看到真实报错信息后修复的准确率远比模糊要求“优化一下”要高得多。还有一个我吃亏后才养成的习惯每次试运行都用pytest -x先跑单条用例全绿之后再跑全量。AI生成的脚本偶尔会有顺序依赖比如某条用例修改了全局状态影响后面用例的结果用-x逐条定位比全量跑完了再回头找要省太多时间。6. 常见问题与排查技巧实录6.1 LLM生成脚本的典型失败模式及应对我梳理了这几个月遇到的高频问题列成了一个速查表方便排查时直接对照现象根因应对方案脚本引用了不存在的模型方法LLM幻觉Prompt里加入“禁止使用不存在的第三方库”或提前写入可用库列表断言只查状态码业务错误漏报缺少few-shot约束在Prompt里提供标准的断言写法案例用例全是happy path无异常分支需求描述没强调边界增加异常路径模板明确要求覆盖生成的代码有语法错误模型上下文窗口过长导致输出截断拆小任务一次只生成一个模块同样输入两次生成结果差异大temperature设置过高将temperature降到0.2以下最棘手的其实是“看起来合理但实际错误的断言”。AI偶尔会编造一个接口字段比如它断言data[count]但接口文档里根本没有这个字段。这种错误静态检查发现不了只有真正运行并比对日志才能看出来。对策是每次试运行后核对响应日志确认断言的字段确实存在、类型正确。6.2 执行环境不一致导致的假失败AI生成的脚本在你自己电脑上跑是绿的推到服务器上就飘红这类问题我在项目里碰到过不下十次。最常见的三个坑是操作系统路径分隔符不同、环境变量缺失、Python版本差异导致typing语法不兼容。解决方式里最管用的不是让AI“写跨平台代码”——它对目标环境的认知是空白——而是在脚本生成之后启动一个统一的静态扫描检查是否存在硬编码路径、未设置默认值的环境变量、以及需要高版本Python才支持的语法特性。这些检查规则可以沉淀到SDK层面每次生成的脚本自动过一遍不合格的返回LLM重写。给AI提供目标环境信息的模板我已经固定了操作系统类型、Python版本、已安装的核心库版本、可写目录路径、环境变量前缀。这些信息拼进系统Prompt后假失败率下降非常明显。6.3 鲁棒性评估中的统计口径与样本量问题最后说一个偏数据向的坑。做水印鲁棒性对比、或者老化测试结果分析时样本量不够会导致结论完全失真。比如你想评估bending攻击下的水印提取正确率只测5张图结果可能偏差非常大按统计学经验在95%置信度、5%误差范围内至少需要约384个样本才能代表整体分布。另一个误区是混淆“平均正确率”和“最差情况正确率”。老化测试里设备在第300轮出现一次重启平均成功率也许是99.9%但这个数据掩盖了系统在第300轮确实崩溃的事实。评测脚本里我会同时输出两类指标评估鲁棒性时以“最差情况”为主要参考。AI生成统计代码的时候默认只算平均我必须在Prompt里点名要P50、P95、P99和最差值缺一不可。还有一个经验对比测试必须保证同一批输入数据不能这次用A图集下次用B图集。AI生成的脚本里如果写了随机种子务必固定下来否则结果无法复现后面所有分析都白做。写在最后的一点体会这套系统从最初“让AI写个脚本试试”到真正能在老化测试和水印评测里稳定跑通中间迭代了很多轮。我个人最大的体会是AI生成测试脚本的瓶颈不在生成能力而在工程约束你的Prompt约定、校验闭环、反馈机制越扎实AI就越可靠你越是把它当一个无所不能的代码生成器越容易被它坑。如果你打算在自己的项目里落地这套思路我的建议是从一个你最熟悉、最重复的测试场景开始先跑通一轮“生成-校验-回灌”把流程和模板固化下来再慢慢扩展。等你手里的结构化Prompt模板和失败case足够多了AI生成的脚本水平会越来越接近团队里熟练工程师的产出。最后再分享一个小技巧每轮调优后把最终通过审核的脚本和对应的Prompt一起存档这比只存档代码价值大得多因为下一次你再遇到类似任务翻出这套配置就等于直接复用了团队的最佳实践。