把需求变更拆成测试用例后,AI 输出稳定了很多
需求变更最容易漏测。尤其是后端接口、小程序活动页、会员权益、订单状态流转这类业务,产品文档里一句“新增一种优惠券适用规则”,落到测试阶段可能就变成十几种边界条件:新老用户、过期时间、叠加规则、退款后状态、库存占用、灰度开关……靠经验当然能补一部分,但很难保证不漏。
我最近更愿意把 ChatGPT、Claude、Gemini、DeepSeek、Grok 这类模型放在“测试用例生成前的拆解环节”,而不是直接让它们给一份最终测试用例。原因很简单:AI 直接生成用例时,看起来很完整,但经常夹杂假设;先让它做规则归纳、风险点枚举、边界条件补全,再由测试或开发确认,落地会稳很多。
下面以一个比较常见的“优惠券规则变更”为例,记录一下我会怎么把 AI 放进测试用例设计流程。
场景:优惠券规则改了,但文档只有几行
假设产品给了这样一段需求:
text
订单结算页支持新优惠券规则: 1. 新增“满 100 减 20”平台券;2. 平台券仅限普通商品使用,虚拟商品不可用;3. 平台券可与会员折扣同时使用;4. 平台券不可与店铺券叠加;5. 订单发生部分退款时,按商品实付金额比例退还优惠金额;6. 活动时间为 2026-01-01 00:00:00 至 2026-01-31 23:59:59;7. 老版本客户端不展示平台券入口,但后端仍需兼容已领取用户的结算请求。这段需求不长,但测试点不少。直接让 AI “生成测试用例”,通常会有两个问题:
- 它会默认一些需求没写清楚的规则,比如“满 100”是否包含运费;
- 它会混合输出“已确定规则”和“建议确认点”,导致评审时很难使用。
所以第一步不要生成用例,而是先做拆解。
第一步:先让 AI 做规则归纳,不要写用例
我会先用这个 Prompt:
text
你是电商后端测试分析助手。请根据下面的需求说明做规则归纳,不要生成测试用例。 输出要求:1. 列出已经明确的业务规则;2. 列出不明确或可能有歧义的点;3. 列出需要产品或开发确认的问题;4. 列出可能影响测试范围的系统模块;5. 不要编造需求中没有提到的规则。 需求如下:【粘贴需求】比较有价值的输出应该是这种结构:
text
已明确规则:- 平台券类型:满 100 减 20;- 适用商品:普通商品;- 不适用商品:虚拟商品;- 可叠加:会员折扣;- 不可叠加:店铺券;- 活动有效期:2026-01-01 00:00:00 ~ 2026-01-31 23:59:59;- 老版本客户端不展示入口,但后端需要兼容结算请求。 不明确点:- 满 100 是否按商品原价、会员折扣后金额、还是订单应付金额判断;- 100 元门槛是否包含运费;- 普通商品和虚拟商品混合下单时,平台券如何分摊;- 部分退款时,优惠金额比例是否四舍五入;- 老版本客户端如何传 platformCouponId;- 店铺券与平台券同时传入时,后端返回错误还是自动择优。这一步的意义是把“可能漏测的地方”提前暴露出来。很多测试遗漏不是因为不会写用例,而是需求阶段没有把规则问清楚。
第二步:把确认后的规则变成测试维度表
等产品和开发确认规则后,再让 AI 生成测试维度,而不是直接生成几十条用例。
假设确认结果如下:
text
补充确认:- 满 100 按会员折扣后的商品实付金额判断,不包含运费;- 普通商品和虚拟商品混合下单时,仅普通商品金额参与门槛计算;- 平台券和店铺券同时传入时,后端返回 400;- 部分退款优惠分摊保留 2 位小数,采用四舍五入;- 老版本客户端如传入 platformCouponId,后端正常计算。Prompt 可以这样写:
text
请根据“原始需求 + 补充确认”生成测试维度表。 要求:1. 不直接生成测试用例;2. 按功能规则、边界条件、异常输入、兼容性、退款场景、时间场景分类;3. 每个维度说明测试目的;4. 标记哪些维度适合自动化测试;5. 输出 Markdown 表格。输出可以整理成这样:
| 分类 | 测试维度 | 测试目的 | 是否适合自动化 |
|---|---|---|---|
| 门槛金额 | 折扣后普通商品金额 = 99.99 | 验证未达到满减门槛 | 是 |
| 门槛金额 | 折扣后普通商品金额 = 100.00 | 验证刚好达到门槛 | 是 |
| 商品类型 | 仅虚拟商品 | 验证不可使用平台券 | 是 |
| 商品类型 | 普通商品 + 虚拟商品 | 验证仅普通商品参与门槛 | 是 |
| 叠加规则 | 平台券 + 会员折扣 | 验证允许叠加 | 是 |
| 叠加规则 | 平台券 + 店铺券 | 验证返回 400 | 是 |
| 退款 | 部分退款普通商品 | 验证优惠金额按比例退还 | 是 |
| 时间 | 活动开始前 1 秒 | 验证不可用 | 是 |
| 时间 | 活动结束后 1 秒 | 验证不可用 | 是 |
| 兼容性 | 老版本客户端传平台券 ID | 验证后端兼容 | 是 |
到这一步,测试范围已经比较清楚了。后面再生成具体用例,会比一开始就让 AI “写测试用例”靠谱得多。
第三步:生成可执行的测试用例草稿
接下来才进入用例生成。Prompt 要尽量约束输出格式,方便后续导入测试管理平台或转成自动化脚本。
text
请根据下面的测试维度生成接口测试用例草稿。 接口背景:- POST /api/order/checkout- 请求参数包含 userId、items、platformCouponId、shopCouponId、clientVersion- 返回 totalAmount、discountAmount、payAmount、errorCode 要求:1. 每条用例包含:用例标题、前置条件、请求输入、预期结果、验证点;2. 只使用已确认规则;3. 金额保留两位小数;4. 异常用例要写明预期 errorCode;5. 输出 JSON 数组,便于后续处理。示例输出可以长这样:
json
[ { "title": "普通商品折扣后金额刚好100元时可使用平台券", "precondition": "用户已领取满100减20平台券,活动处于有效期内", "request": { "userId": "user_001", "items": [ { "skuId": "normal_sku_001", "type": "NORMAL", "originAmount": "125.00", "memberDiscountAmount": "25.00" } ], "platformCouponId": "platform_coupon_001", "shopCouponId": null, "clientVersion": "3.8.0" }, "expected": { "discountAmount": "20.00", "payAmount": "80.00", "errorCode": null }, "checkpoints": [ "会员折扣后普通商品金额为100.00", "平台券满减生效", "最终应付金额为80.00" ] }, { "title": "平台券和店铺券同时传入时返回错误", "precondition": "用户同时拥有平台券和店铺券", "request": { "userId": "user_001", "items": [ { "skuId": "normal_sku_001", "type": "NORMAL", "originAmount": "120.00", "memberDiscountAmount": "0.00" } ], "platformCouponId": "platform_coupon_001", "shopCouponId": "shop_coupon_001", "clientVersion": "3.8.0" }, "expected": { "errorCode": "COUPON_CANNOT_STACK" }, "checkpoints": [ "后端不自动择优", "返回明确错误码", "不生成结算结果" ] }]这类 JSON 不能直接算“最终测试用例”,但已经可以作为测试评审材料,也可以继续转成自动化测试脚本。
第四步:把用例转成自动化测试骨架
如果项目是 Java + JUnit,可以让 AI 根据 JSON 草稿生成测试骨架。这里仍然要注意:让它生成“骨架”,不要让它替你虚构项目里的 Fixture、Repository 或 Mock 细节。
Prompt:
text
请根据下面的接口测试用例 JSON,生成 JUnit 5 测试骨架。 限制:1. 不要编造项目中不存在的工具类;2. 用伪方法表示数据准备,例如 createUser、createCoupon、createSku;3. 重点体现测试结构、断言和边界;4. 金额使用 BigDecimal;5. 输出 Java 代码。示例代码:
java
class OrderCheckoutCouponTest { @Test void shouldApplyPlatformCouponWhenDiscountedNormalAmountEquals100() { // given User user = createUser("user_001"); Coupon platformCoupon = createPlatformCoupon("platform_coupon_001", "100.00", "20.00"); Sku normalSku = createSku("normal_sku_001", ProductType.NORMAL, new BigDecimal("125.00")); createMemberDiscount(user, normalSku, new BigDecimal("25.00")); CheckoutRequest request = new CheckoutRequest(); request.setUserId(user.getId()); request.setItems(List.of(new CheckoutItem(normalSku.getId(), 1))); request.setPlatformCouponId(platformCoupon.getId()); request.setShopCouponId(null); request.setClientVersion("3.8.0"); // when CheckoutResponse response = orderCheckoutService.checkout(request); // then assertNull(response.getErrorCode()); assertEquals(new BigDecimal("20.00"), response.getDiscountAmount()); assertEquals(new BigDecimal("80.00"), response.getPayAmount()); } @Test void shouldRejectWhenPlatformCouponAndShopCouponUsedTogether() { // given User user = createUser("user_001"); Coupon platformCoupon = createPlatformCoupon("platform_coupon_001", "100.00", "20.00"); Coupon shopCoupon = createShopCoupon("shop_coupon_001"); Sku normalSku = createSku("normal_sku_001", ProductType.NORMAL, new BigDecimal("120.00")); CheckoutRequest request = new CheckoutRequest(); request.setUserId(user.getId()); request.setItems(List.of(new CheckoutItem(normalSku.getId(), 1))); request.setPlatformCouponId(platformCoupon.getId()); request.setShopCouponId(shopCoupon.getId()); request.setClientVersion("3.8.0"); // when CheckoutResponse response = orderCheckoutService.checkout(request); // then assertEquals("COUPON_CANNOT_STACK", response.getErrorCode()); assertNull(response.getPayAmount()); } private User createUser(String userId) { // TODO: replace with project fixture return null; } private Coupon createPlatformCoupon(String couponId, String threshold, String discount) { // TODO: replace with project fixture return null; } private Coupon createShopCoupon(String couponId) { // TODO: replace with project fixture return null; } private Sku createSku(String skuId, ProductType type, BigDecimal price) { // TODO: replace with project fixture return null; } private void createMemberDiscount(User user, Sku sku, BigDecimal discountAmount) { // TODO: replace with project fixture }}这段代码的价值不是直接复制运行,而是给测试开发一个清晰的结构:数据准备、请求构造、调用接口、断言结果。真实落地时,要替换成项目里的测试基类、Fixture、Mock Server 或 Testcontainers 环境。
不同模型适合放在哪些环节
这类任务可以不指定某一个模型。我的经验是,按阶段使用会更自然:
- Claude:适合读较长需求、PRD、评审纪要,归纳规则比较细;
- ChatGPT:适合把确认后的规则转成用例、脚本和代码骨架;
- Gemini:适合表格化整理、多份资料合并、生成结构化清单;
- DeepSeek:适合中文技术语境下解释业务逻辑、补充边界条件;
- Grok:适合提出一些非常规风险,比如历史客户端、异常状态、灰度开关;
- Seedance 2.0:更适合把复杂流程做成短视频分镜或产品演示,不适合直接判断测试结论;
- ChatGPT Image 2.0:适合生成流程图、测试覆盖图、技术文章配图,但图片涉及商用时要检查版权、商标、隐私和平台规范。
多模型对比不是为了找“标准答案”。同一份需求交给不同模型看,重点是观察它们分别漏掉了什么。测试设计最怕盲区,多一个视角往往比多十条重复用例更有用。
AI 生成测试用例后,怎么验证
AI 给出的用例一定要过几道检查。
1. 对照需求确认记录
所有用例都要能追溯到需求原文或补充确认。
如果模型写出“平台券可与运费券叠加”,但需求里没说,那就必须删掉或标记为待确认。
2. 对照接口定义
字段名、错误码、金额格式、状态枚举,都要回到接口文档、OpenAPI、后端 DTO 中确认。AI 很容易写出看起来合理但项目里不存在的字段。
可以用一个简单脚本检查用例 JSON 中的字段是否超出接口定义:
python
allowed_request_fields = { "userId", "items", "platformCouponId", "shopCouponId", "clientVersion"} test_cases = [ { "title": "平台券和店铺券同时传入时返回错误", "request": { "userId": "user_001", "items": [], "platformCouponId": "platform_coupon_001", "shopCouponId": "shop_coupon_001", "clientVersion": "3.8.0", "couponType": "PLATFORM" } }] for case in test_cases: extra_fields = set(case["request"].keys()) - allowed_request_fields if extra_fields: print(f"[WARN] {case['title']} contains unknown fields: {extra_fields}")输出:
text
[WARN] 平台券和店铺券同时传入时返回错误 contains unknown fields: {'couponType'}这种检查很简单,但能拦住不少“AI 自己补字段”的问题。
3. 对照代码实现
如果后端实际逻辑是先计算平台券再计算会员折扣,而需求确认是“会员折扣后金额判断门槛”,那就不是测试用例问题,而是实现和需求不一致。测试设计阶段发现这类差异,成本最低。
4. 对照历史兼容
老版本客户端、新老接口、灰度开关、历史订单状态,AI 默认不一定会想到。涉及兼容场景时,最好把版本矩阵单独列出来。
例如:
| clientVersion | 是否展示平台券入口 | 是否允许传 platformCouponId | 预期 |
|---|---|---|---|
| 3.6.0 | 否 | 是 | 后端兼容计算 |
| 3.8.0 | 是 | 是 | 正常计算 |
| 3.8.0 | 是 | 否 | 不使用平台券 |
多模型工具怎么选,不看模型数量看流程适配
如果团队想把 AI 放进测试或研发流程,我会更关注这些点:
- 是否方便把同一份需求交给不同模型对比;
- 是否支持较长上下文,能放下 PRD、接口文档、错误码和评审纪要;
- 是否方便导出 Markdown、JSON、CSV;
- 是否支持会话留存,方便追溯某次用例设计的依据;
- 是否能区分草稿、确认项、待确认项;
- 是否方便做输入脱敏;
- 是否支持文本、图像、视频等多模态任务,便于后续做流程图、培训素材或演示材料;
- 是否适合团队协作,而不是只服务个人聊天。
模型能力当然重要,但测试工作更看重可追溯、可复核、可执行。不能导出、不能沉淀、无法复盘的输出,很难进入团队流程。
风险边界:这些内容别直接丢给 AI
测试和研发资料里经常包含敏感信息,使用 AI 前最好做脱敏。
不要直接提交:
- 真实用户手机号、地址、身份证、邮箱;
- 生产订单号、支付流水号、退款单号;
- 内部接口域名、数据库地址、访问 Token;
- 完整生产日志;
- 未公开活动策略或商业规则;
- 客户名称、合同编号、渠道分成信息;
- 安全漏洞细节。
可以把真实值替换成占位符:
text
user_001order_001coupon_001example.internaltoken_xxxAI 需要理解的是规则结构,不需要知道真实业务数据。
如果后续用图像或视频模型生成测试流程图、培训视频、产品演示,也要检查截图、Logo、商标、人物肖像、字体授权和平台发布规范。多模态结果看起来更直观,但泄露信息的风险也更隐蔽。
FAQ:几个常见误区
1. AI 生成的测试用例能直接放进测试管理平台吗?
不建议直接放。至少要经过需求追溯、字段校验、错误码确认和人工 Review。AI 输出更适合作为草稿和覆盖面补充。
2. 单一模型够不够?
普通需求够用。涉及复杂金额、权限、兼容、历史状态时,建议用两个以上模型交叉看一遍,重点不是比较文风,而是找遗漏点。
3. Prompt 怎么写会更稳定?
不要只写“帮我生成测试用例”。更稳的方式是分三步:先归纳规则,再列不确定点,最后生成可执行用例。每一步都限制它不要编造规则。
4. 如何避免 AI 编造接口字段?
让模型只基于接口文档输出,并要求每个字段标注来源。生成后再用脚本或人工对照 OpenAPI、DTO、接口文档检查。
5. AI 会不会替代测试设计?
短期看更像增强工具。它能补充边界条件、整理规则、生成草稿,但判断业务风险、确认优先级、设计回归策略,仍然需要测试和研发共同负责。
总结
把 AI 用在测试用例生成里,关键不是让它一次性给最终答案,而是把任务拆开:需求规则归纳、歧义点确认、测试维度整理、用例草稿生成、自动化骨架生成、结果验证。
对于 CSDN 读者来说,更推荐从一个高频、低风险、可验证的需求变更开始试。Prompt 写清楚边界,输入资料先脱敏,输出结果必须对照需求、接口、代码和测试环境验证。重要场景可以用多模型交叉检查,但最终结论仍然要由团队确认。
AI 可以帮我们少漏一些测试点,也能减少整理用例的重复劳动。但测试质量不会因为用了模型自动变好,真正起作用的还是清晰规则、可执行用例和稳定的验证闭环。