前两天帮一个团队评审功能测试用例,说实话,看完前五十条我就明白了问题出在哪:200多条用例里,有37条的断言根本没法执行,有60多条直接把需求描述原封不动复制成了预期结果。问测试同事为什么这么写,答案是内容太多,需求当天改了三版,来不及逐条重写。
这个场景我非常熟悉。最近一年多,生成式AI确实让测试用例产出速度上了一个台阶,但我也发现一个扎心的规律:直接让大模型“裸写”出来的测试用例,看着整齐,实际用起来处处是坑。真正让生成式AI在测试领域发挥价值的,从来不是模型本身有多强,而是你给它设计的自定义规则有没有把业务约束、数据边界、输出格式想清楚。
这篇文章我想完整聊聊自定义规则这件事,不绕弯子,从为什么裸奔的AI写不好用例,到一套能落地的规则体系长什么样,再到怎么把规则真正接进生成流程,最后把我实测踩过的坑一并倒出来。适合正在试水 AI 用例生成、或者已经遇到底层质量问题的测试团队,也适合想做测试提效平台的开发同学参考。
1. 为什么裸奔的生成式AI写不好测试用例
先说结论:不是生成式AI能力不行,是它在没有任何约束的情况下,会按“语言惯性”而非“业务逻辑”来写用例。
1.1 一份AI直接生成的典型用例,问题在哪
我拿一个很常见的“用户下单”模块做过实验。直接把需求文档发给大模型,让它生成订单相关的测试用例,产出的第一条长这样:
用例编号:TC_ORDER_001
前置条件:用户已登录
步骤:点击下单按钮
预期结果:弹出下单成功提示
看起来没什么毛病?但细拆全是问题:
- 断言无法执行。“弹出下单成功提示”只是表象,真正应该验证的是订单状态变成“待支付”、库存被锁定、订单号生成规则对不对、支付链接跳转参数有没有带上。把这句“预期结果”交给自动化脚本,脚本根本不知道该断言哪个接口字段。
- 需求漂移。需求文档里明确写了“下单时若库存不足,提示用户并跳转到相似商品推荐页”,但AI 忽略了这句,因为这段文字藏在文档第 14 页的边角。它更倾向于生成常见电商语义下的“标准下单流程”。
- 粒度失衡。要么一个“点击下单按钮”就完事,要么把“用户登录”从打开 App、输入账号、输入密码拆成二十条用例,接近崩溃。
- 数据和场景固化。AI 自动生成的用例里,几乎全是“正常登录-正常下单-正常支付”的黄金路径,边界值、空值、并发场景、取消订单后的逆向流程,统统缺席。
- 没有可追溯性。用例和需求条目之间没有对应关系,出了问题你根本说不清这条用例是在验证哪一条需求。
这些问题不是偶发,而是必然——因为大模型训练时见过太多“订单测试用例长什么样”的语料,它会在统计概率上选择一个“看起来最像用例”的输出,而不是基于你的具体需求做推理。
1.2 本质原因:生成式AI是“快”,不是“准”
我曾经把 AI 生成测试用例比作一个新来的实习生:他读文档速度非常快,也读过很多业界的用例模板,但你如果只丢一句“帮我写一下订单模块的用例”,他写出来的东西一定是最常见、最安全、最不费脑的版本。你要让他产出符合你团队规范、贴合你业务状态机、能直接落到自动化框架里的用例,就必须给他一本“工作手册”。
这本“工作手册”就是我说的自定义规则。它的存在不是为了限制 AI,而是为了让 AI 在生成用例之前,就把以下四件事确定下来:
- 业务规则是什么:状态怎么流转,哪些分支必须覆盖,哪些异常必须考虑;
- 数据规则是什么:哪些字段有哪些候选值,边界数据如何取;
- 格式规则是什么:用例编号怎么编排、断言如何措辞、输出用什么结构;
- 质量红线是什么:哪些步骤禁止合并,哪些预期结果必须拆条。
我在实际带团队的过程中发现,凡是觉得“生成式AI写测试用例是智商税”的人,九成都是在规则缺位的情况下直接裸奔使用的。一旦把规则补齐,情况会完全不同。
2. 一套能落地的自定义规则体系,应该包含哪几层
很多人一听到“自定义规则”就以为是在提示词里加一句“请按照规范生成”。真实情况远没这么简单。一套能在生产中跑起来的规则体系,至少分成四个维度,缺一不可。
| 规则维度 | 解决什么问题 | 典型内容 |
|---|---|---|
| 全局规则 | AI 的底层语言习惯与格式纪律 | 命名规范、用例编号规则、描述风格、禁用词 |
| 业务规则 | 需求背后的真实业务逻辑 | 状态机、分支覆盖要求、异常路径、需求对应关系 |
| 数据规则 | 用例执行时用的数据怎么来 | 数据字典、枚举值、边界值策略、组合规则 |
| 输出规则 | 生成结果能否被后续工具消费 | 输出模板、断言要求、字段顺序、用例数量控制 |
2.1 全局规则:先管住 AI 的“表达欲”
全局规则解决的是 AI 输出里最常见也最顽固的问题:它总爱按自己语言的舒适区来写,而不是按你的工程规范来写。
我见过最多的两种:
一是“散文式用例”。AI 会把前置条件写成一段话,把执行步骤写成带语气的自然语言,比如“用户在页面上找到那个大大的红色按钮并点击它”。这对人看当然没问题,但一旦要让自动化脚本或者同事评审,就很难办。
二是“同义反复式断言”。把“页面跳转到订单详情页”写成“用户可以看到一个全新的订单详情页面呈现在眼前”,动词、名词、判定标准全都没有固定形式。
所以全局规则里我会强制定义:
global: language: zh-CN style: 祈使句,无主语,禁止形容词修饰 id_prefix: TC step_verb_whitelist: [点击, 输入, 选择, 等待, 断言, 跳转, 上传, 下载] forbidden_words: [可能, 应该, 或许, 大概, 正常显示, 正确提示]这份规则单独放在规则文件最前面,让 AI 在生成任何一条用例之前就先过一遍语言过滤。实际执行下来,输出质量提升最明显的就是这一层。
2.2 业务规则:把“需求语言”翻译成“测试逻辑”
业务规则是整个自定义规则体系里最核心、也最花功夫的部分。它不是从通用测试理论里抄的,而是完全从你的业务文档、状态图、接口定义中提炼出来的。
以电商订单为例,一个简单的业务规则文件长这样:
business: entities: - name: order fields: - { name: status, enum: [待支付, 待发货, 已发货, 已完成, 已取消] } - { name: amount, type: decimal, min: 0.01, max: 99999.99 } state_machine: - { from: 待支付, to: 待发货, action: 支付成功 } - { from: 待支付, to: 已取消, action: 超时未支付 } - { from: 待发货, to: 已发货, action: 管理员发货 } - { from: 已发货, to: 已完成, action: 用户确认收货 } - { from: 待支付, to: 已取消, action: 用户取消 } cover_requirements: mandatory注意看,业务规则不是让 AI 去“理解”订单流程,而是直接把状态机模型喂给它。这样生成的用例,天然是沿着状态迁移路径走的,而不是沿着“页面长什么样”走的。
2.3 数据规则:消灭“正常值偏好”
大模型有个很有意思的偏好:你如果不指定数据,它生成用例时默认会把所有输入都填成正常值——手机号是13800138000,金额是100,日期是今天,数量是1。这种“正常值偏好”让异常类和边界类用例的产出率极低。
数据规则要做的就是强制打破这种偏好:
data: boundaries: amount: [0.01, 0, -1, 99999.99, 100000, null] enum_sets: pay_channel: [alipay, wechat_pay, union_pay, null] generation_strategy: rule: 每种边界值至少覆盖一条用例,禁止全部使用正常值 dictionary: - { field: phone, candidates: [13800138000, 12345678901, null], note: 含非标准格式 }有了这份数据规则,AI 生成用例时会强制在每条用例里标注“数据来源是boundary_01还是enum_sets”,而不是自己凭空编造。
2.4 输出规则:让产物可以直接进工具链
最后这一层最容易被忽略,但它决定生成结果能不能被下游自动化和评审工具消费。输出规则要明确到字段级别:
output: template: | 用例编号: {{id}} 关联需求: {{requirement}} 前置条件: {{precondition}} 执行步骤: {{for step in steps}} - {{step.action}}({{step.target}}) {{endfor}} 测试数据: {{data_source}} 预期结果: {{expectation}} expectations: - 每条用例至少一条可编程断言 - 断言必须引用具体接口字段或页面元素标识 - 用例数量: 单功能模块不超过30条,超过自动合并 dynamic_seed: require这一层同时是在给 AI 划“格式电网”。很多团队的规则文件只写到业务层就停了,导致生成结果人看着没问题,但完全没法script化。加上输出规则后,我基本可以直接拿着生成结果往自动化脚手架里灌。
3. 把自定义规则真正接进生成流程的三种姿势
规则文件设计好了,接下来就是工程问题:怎么让规则进入生成流程?我测试过三种方式,各有适用场景,这里都说清楚。
3.1 姿势一:规则作为上下文直接注入(适合快速验证)
最朴素的做法,是把规则文件整体打包,作为系统提示词的一部分发给模型。具体操作是把规则文件拆成“规则摘要 + 业务规则全文 + 数据规则 + 输出模板”,按顺序拼在 prompt 里。
优点:落地快,半小时就能跑通,适合团队刚开始探索的阶段。
缺点:一旦规则文件超过一定长度,模型会出现“规则稀释”现象——前面和后面的规则记得住,中间的容易丢。这个我在第 4 章会展开讲。
为了降低稀释概率,可以在规则前面加一行“规则执行摘要”:
请严格按以下规则生成用例:全局规则保证语言风格,业务规则保证覆盖路径,数据规则保证边界场景,输出规则保证结构。与规则冲突时,取优先级高者,严格执行不得跳过。3.2 姿势二:规则驱动生成后校验(适合质量闭环)
第二种方式是把规则从“生成时约束”变成“生成后校验”。生成完成后,用轻量脚本对结果做规则扫描,不合格直接打回重生成。
我一般用一个几百行的 Python 脚本处理校验,核心逻辑很简单:
import re import yaml def load_rules(rule_file): with open(rule_file, "r") as f: return yaml.safe_load(f) def validate_use_case(uc, rules): errors = [] global_rules = rules.get("global", {}) if not re.match(rf"^{global_rules['id_prefix']}_", uc["id"]): errors.append(f"用例编号不符合前缀规范: {uc['id']}") for word in global_rules.get("forbidden_words", []): if word in uc["expected"]: errors.append(f"期望结果包含禁用词: {word}") if len(uc["steps"]) > 6: errors.append("步骤数超过6条,需要拆解") return errors # 生成 -> 校验 -> 不合格重新生成 的循环 for raw in generated_cases: errs = validate_use_case(raw, rules) if errs: regenerate(raw, errs)这样做的好处是把质量门槛变成工程约束,而不是靠模型自觉。缺点是校验规则如果写得不够精确,可能出现“打回-重写-再打回”的死循环,所以我会把校验错误信息直接拼进重生成的 prompt 里,让模型明确知道哪里不合格。
3.3 姿势三:装配式生成(最推荐的做法)
第三种方法是我现在团队主力使用的方式,逻辑上叫“装配式规则生成”:把用例拆成骨架、数据、断言三个独立部分,分别由规则驱动生成,再在脚本层装配成完整用例。
具体流程是这样的:
- 根据业务规则生成“逻辑场景列表”,比如“待支付状态-超时15分钟未支付-取消订单”;
- 根据数据规则,为每个场景匹配对应的数据标签;
- 根据输出规则,生成用例的格式骨架;
- 在脚本里把三部分拼装起来,形成最终用例文档。
拿“订单支付超时”场景线上对比一下:
直接生成的结果:
用例编号:TC_001
前置条件:已下单
步骤:等15分钟
预期结果:订单取消
装配式生成的结果:
用例编号:TC_ORDER_TIMEOUT_001
关联需求:REQ-ORDER-102
前置条件:订单状态=待支付, 创建时间=15分钟前, 支付渠道=alipay
执行步骤:
- 请求“查询订单”接口(GET /api/order/{id})
- 断言响应码=200
- 断言 status=已取消
- 断言 cancel_reason=PAY_TIMEOUT
测试数据:boundary_amount_0.01
预期结果:订单状态流转为已取消, 取消原因明确, 返回码与响应结构符合 API 规范
区别很明显:后者每条信息背后都有明确出处,数据来自数据规则,状态来自状态机,断言来自字段定义,没有任何一条是 AI 拍脑袋编的。
4. 实测最容易翻车的五个点及补救方案
规则体系搭起来之后,不等于就能稳定产出高质量用例。我在跑生成流程时翻过不少车,下面五个问题是最常见的,全部有现场、有原因、有补救方案。
| 翻车现场 | 根因 | 补救方案 |
|---|---|---|
| 让它按 Markdown 表格输出,结果某几次变成列表 | 输出模板只在 prompt 里出现一次,模型遵守不彻底 | 输出规则改为“严格模板占位符”,外加脚本校验兜底 |
| 要求覆盖所有异常分支,又要求单模块不超过30条,结果两者冲突 | 规则之间没有优先级 | 规则文件增加 priority 字段,高优先级覆盖低优先级 |
| 规则文件超过2000字后,中间部分规则被无视 | 上下文过长导致注意力稀释 | 重要规则前置、写规则执行摘要、按模块拆分生成 |
| 断言“看起来对”但脚本无法执行 | 断言太泛,没有落到具体字段 | 输出规则强制引用接口字段,禁用“正常”“正确”类形容词 |
| 生成结果“太像人写的”,自动追加解释了需求背景 | 模型觉得在帮你,其实在干扰 | 在全局规则中增加“禁止输出需求分析,只输出用例本身” |
4.1 格式漂移:AI 的不稳定输出契约
这是所有问题里最磨人的。同一套规则,同一段 prompt,连续跑十次,大概率有一次输出的表格结构就崩了——多了一列,少了一行,表头措辞变了。尤其当你换模型版本或者调整温度参数后,漂移会更明显。
我试过在 prompt 里反复强调“必须输出表格”,效果有限。真正治本的方式,是把输出规则从“自然语言描述”改成“不可协商的代码模板”,并用校验脚本做最后一道锁。只要脚本发现格式不符合模板,就直接判定不合格,重新生成,不给人眼留下筛选负担。
顺带提一句:有的人会去找那种宣称“无限制、无审核”的生成工具来跑测试用例,实测下来这类输出在我这套规则体系里基本属于不可用项。它们通常没有稳定的输出格式契约,模型版本一变,用例结构跟着变,规则校验脚本根本没法做匹配。做测试提效,稳定性优先于自由度。
4.2 规则冲突:没有优先级就是一堆废纸
当规则文件写到上百条,你一定会遇到两条规则互相打架的场景。比如:业务规则要求“所有状态迁移路径必须全部覆盖”,同时全局规则限定“单模块用例数不超过30”,结果状态机光路径就有40条,直接冲突。
我的做法是给每条规则加优先级字段,数值越大越优先:
rules: - name: 状态迁移全覆盖 priority: 90 - name: 单模块用例数限制 priority: 50冲突时,优先执行高优先级规则,低优先级规则自动降级为“尽量满足”。这样模型就不会陷入二选一的死循环,而是知道该听谁的。
4.3 规则稀释:中间内容被忽略的注意力陷阱
大模型处理超长上下文时,注意力天然集中在开头和结尾,中间内容容易被“选择性失明”。这是模型的底层注意力机制决定的,不是提示词写得不够好。
我踩过的坑是:把数据规则放在整个规则文件的中部,结果生成的用例里,边界值相关用例只占应覆盖率的不到两成,后面检查数据来源标签才意识到 AI 压根没读到那一层。
补救对策有三个:
- 把最重要的规则(尤其是业务状态机)放到 prompt 开头位置;
- 在 prompt 开头写清楚“本规则文件共有四层,每一层都必须执行”;
- 复杂项目把规则拆成两个文件:一个精简的“执行摘要”随生成请求发送,一个完整的规则库作为离线上下文参照物。
4.4 断言空转:看起来有断言实际上没断言
这是最危险的一个坑,因为从文档上看完全正常,直到你把用例交给自动化脚本执行时才发现,断言语义根本映射不到任何实际的接口字段或定位器上。
举个例子,AI 生成的断言可能是“页面显示订单提交成功”,而真实的自动化脚本里,你需要的是:
assert response.json()["data"]["order_status"] == "CREATED"所以我在输出规则里加了一条硬性要求:每一条预期结果必须以“对某字段或某元素的断言”形式出现,并且字段名必须来自接口文档或页面组件库的已知清单。AI 一旦试图自创字段名,校验脚本立刻拦截。
4.5 叙述癖:AI 忍不住附加需求解读
刚开始跑通流程的时候,生成的用例文件里经常莫名多出一段“本模块主要功能是……”或者“该异常场景说明用户在极端情况下会……”这种废话。从文字质量看没什么问题,但对测试用例来说,这就是纯噪音。
我的解决方案是在全局规则中加入一条:
global: no_analysis: true no_summary: true并在 prompt 末尾再次强调:输出内容只允许出现规则模板定义的结构,任何不在模板中的内容一律视为错误。
5. 三类典型测试场景的规则定制思路
规则体系是通用的,落地时还要针对不同测试类型做取舍。下面是我在接口测试、Web端到端测试和复杂状态流转测试这三类场景里的实际定制思路。
5.1 接口测试规则:锁定参数组合、返回码和幂等性
接口测试和 UI 测试最大的区别在于:输入输出都是结构化数据,规则更容易精确表达。所以接口类的规则文件,我通常会把重点放在三个地方:
- 参数组合规则:哪些参数必填、哪些互斥、哪些联动、哪些必须为空;
- 返回码规则:2xx/4xx/5xx 各种路径对应哪些业务原因;
- 幂等性规则:哪些接口需要校验重复请求,重复提交时返回的是“成功”还是“已知状态”。
举个例子,创建一个“退款接口”的用例规则片段:
api: path: /api/refund parameter_rules: - { field: order_id, required: true, rule: 必须是已支付状态的订单号 } - { field: amount, required: true, rule: 金额不能大于订单实付金额 } - { field: reason, required: false, rule: 默认取 REFUND_USER_REQUEST } response_code_map: - { code: 400, scene: 订单号非法 } - { code: 409, scene: 订单状态不允许退款 } - { code: 200, scene: 退款申请成功 }接口场景下,生成式AI的价值在于快速生成大量参数组合和路径覆盖,但每条组合的逻辑正确性完全靠参数规则兜底。如果不把这些规则表达清楚,AI 就会用想象去补全。
5.2 Web端到端测试规则:让用例从诞生起就适配自动化
今年团队在全面铺 Playwright,所以我在 Web 端到端的规则定制上格外注意“可自动执行性”。比如有一条规则是这样的:
web_e2e: selector_rule: - 优先使用 [data-testid] 定位 - 禁止使用文本内容定位 wait_rule: - 统一使用显式等待 - 禁止出现 sleep(3) 这种固定等待 evidence_rule: - 每条失败用例必须自动截图 - 截图位置统一落到 artifacts/ 目录 step_rule: - 每一条执行步骤都必须能映射到 Playwright 的 action这里的关键是:规则的作用不只是让 AI 产出的用例“能看”,而是让它产出后能直接变成 Playwright 脚本的注释级蓝本。我测试过,加了这套规则之后,把 AI 生成的用例转成自动化脚本的工时,从平均每人每天40分钟降到了15分钟左右。
5.3 复杂流程/状态流转测试规则:把状态机放进去,而不是放流程图
很多测试团队喜欢让 AI“根据业务流程图生成用例”,但说实话,大模型对标准流程图的解析能力非常一般,特别是分支多、环状迁移多的图,几乎一定会漏路径。
我的做法是:抛弃图片,改用状态机文本描述。这个观点可能跟直觉不太一样,但文本状态机对 AI 来说天然易读、易拼装路径。
以订单生命周期为例,状态机文件长这样:
states: - 待支付 - 待发货 - 已发货 - 已完成 - 已取消 transitions: - { from: 待支付, to: 待发货, action: 支付 } - { from: 待支付, to: 已取消, action: 超时 } - { from: 待支付, to: 已取消, action: 手动取消 } - { from: 待发货, to: 已发货, action: 发货 } - { from: 已发货, to: 已完成, action: 收货 } coverage: - 每个状态至少进入一次 - 每个动作至少触发一次 - 必须包含从创建到终态的最长路径和最短路径最终生成的用例会沿着状态机路径跑,而不是像裸生成时那样永远围绕“登录-下单-支付”这条最舒服的路径打转。很多团队把用例数量翻了三倍,覆盖路径反而更全,差别就出在这一层。
最后分享一条我最近才真正悟透的经验:自定义规则文件本身也应该被当成代码来管理,进版本库、留评审记录、绑定需求变更。因为需求一改,状态机就要改,状态机一改,规则文件就要跟着改,规则文件失效的速度往往比你想象得快。我踩过最大的坑不是规则设计得不好,而是规则三个月没有更新,团队还拿它当圣旨用。
如果你正准备在团队里引入生成式AI写测试用例,我的建议很直接:先拿一个模块,把规则文件写到十条以内,跑通一轮,再把数据规则和输出规则逐渐加厚。别一上来就追求大而全,规则体系是长出来的,不是憋出来的。