从需求与接口文档生成测试用例

测试用例最贵的部分是「想全」。需求条目、接口文档(OpenAPI/protobuf)、权限矩阵、历史缺陷单都是模型擅长处理的文本;让 AI 把它们摊成用例清单,人负责删掉幻觉、补上判定依据。

1. 输入准备:把文档裁成可判定的片段

不要整篇丢进去。按接口切开,一次一份,并明确字段的合法取值范围。

# openapi/order.yaml 片段
paths:
  /api/orders:
    post:
      parameters: [{ in: header, name: Idempotency-Key, required: false }]
      requestBody:
        required: true
        schema: { required: [sku, qty], sku: { minLength: 1, maxLength: 32 }, qty: { minimum: 1, maximum: 999 } }
      responses: { "201": 创建成功, "400": 参数非法, "409": 库存不足 }
角色:测试工程师。
输入:上面的接口片段 + 业务规则(同一用户对同一 sku 每日限购 10 件;带相同
Idempotency-Key 的重复请求必须返回首次结果,且不重复扣库存)。
任务:生成用例清单,用 Markdown 表格输出。
要求:
1. 分类固定为:正常、边界、异常、权限、幂等;
2. 每行含:用例 ID、分类、前置、输入、期望结果、判定依据、优先级、是否可自动化;
3. 期望结果必须可判定(状态码 + 响应体关键字段),不许写「正常返回」;
4. 只使用文档中出现的字段与状态码;没覆盖的判断点单列「待确认」;
5. 不要输出测试代码。

第 4 条最关键:模型很愿意「帮你想一个合理的字段」,比如给订单补个 coupon_code、给错误编一个 422。

2. 用例表长什么样

ID分类前置输入期望结果判定依据优先级
ORD-01正常库存 100sku=A1, qty=1201,id 非空,库存 99OpenAPI 201P0
ORD-02边界库存 100qty=1(最小值)201minimum: 1P0
ORD-03边界库存 100qty=999(最大值)201maximum: 999P1
ORD-04边界库存 100qty=1000400,error=PARAM_INVALIDmaximum: 999P0
ORD-05边界今日已下 9 件qty=1201(恰好到限购)限购 10 件P0
ORD-06边界今日已下 10 件qty=1400,error=LIMIT_EXCEEDED限购 10 件P0
ORD-07异常库存 0qty=1409,error=OUT_OF_STOCKOpenAPI 409P0
ORD-08异常—缺 sku 字段400,不是 500required: [sku, qty]P0
ORD-09异常—sku 长度 33400maxLength: 32P1
ORD-10权限未带令牌合法 body401鉴权中间件P0
ORD-11权限令牌属于他人查询他人订单404,不泄露存在性越权防护P0
ORD-12幂等已有 Key=K1 的成功请求同 Key 重放返回首次结果,库存只扣一次幂等规则P0
ORD-13幂等并发发起 2 个同 Key 请求同时提交仅 1 个 201,另 1 个返回同一结果幂等 + 并发P0

「判定依据」是人工必填项:它逼你把「为什么是这个期望值」落到文档的某一行,而不是模型的印象。

3. 用例转测试骨架

清单确认后再转代码,一次一个分类,避免生成一大坨跑不起来的东西。

角色:Go 测试工程师。
输入:表格里的 ORD-01 ~ ORD-09。
要求:
1. 用 httptest + 表驱动,name 直接用用例 ID 与描述;
2. 只生成骨架:请求构造、状态码断言、响应体关键字段断言;
3. 库存与限购计数通过替身的初始状态控制;
4. 表格里没有的用例不要自行添加。
func TestCreateOrder(t *testing.T) {
	cases := []struct {
		id, desc        string
		stock, todayQty int
		body            string
		wantCode        int
		wantErr         string
	}{
		{"ORD-01", "正常创建", 100, 0, `{"sku":"A1","qty":1}`, 201, ""},
		{"ORD-04", "超最大数量", 100, 0, `{"sku":"A1","qty":1000}`, 400, "PARAM_INVALID"},
		{"ORD-06", "超出限购", 100, 10, `{"sku":"A1","qty":1}`, 400, "LIMIT_EXCEEDED"},
		{"ORD-07", "库存不足", 0, 0, `{"sku":"A1","qty":1}`, 409, "OUT_OF_STOCK"},
		{"ORD-08", "缺少 sku", 100, 0, `{"qty":1}`, 400, "PARAM_INVALID"},
	}
	for _, tc := range cases {
		t.Run(tc.id+" "+tc.desc, func(t *testing.T) {
			r := newTestRouter(tc.stock, tc.todayQty)
			req := httptest.NewRequest(http.MethodPost, "/api/orders", strings.NewReader(tc.body))
			req.Header.Set("Content-Type", "application/json")
			req.Header.Set("Authorization", "Bearer "+testToken)
			rec := httptest.NewRecorder()
			r.ServeHTTP(rec, req)

			if rec.Code != tc.wantCode {
				t.Fatalf("code = %d, want %d, body = %s", rec.Code, tc.wantCode, rec.Body.String())
			}
			var got struct{ Error string `json:"error"` }
			_ = json.Unmarshal(rec.Body.Bytes(), &got)
			if tc.wantErr != "" && got.Error != tc.wantErr {
				t.Errorf("error = %q, want %q", got.Error, tc.wantErr)
			}
		})
	}
}

4. AI 擅长与人工必补

维度AI 表现人工必补
正常路径全是否有多个业务分支(新客/老客)
边界值强(min/max 直接读文档)业务侧隐式边界(限购、额度、时段)
异常路径中(易编造状态码)错误码契约、是否泄露内部信息
权限弱(文档通常没写)越权、角色矩阵、数据归属
幂等与并发弱幂等键、重复请求、并发同键
数据一致性弱库存与账户扣减只发生一次的验证

5. 常见坑

  • 幻觉字段与状态码:模型补出文档里没有的 coupon_code、422。验收时把生成的字段名逐个回查 OpenAPI,不存在的删掉。
  • 不可判定的期望:出现「正常返回」「应当合理」这类描述,用例等于没写;判定依据必须能指向文档行或业务规则。
  • 把实现当需求:直接贴代码让 AI 生成用例,得到的是「实现现在怎么做」而不是「需求要求怎么做」,缺陷会被继承。
  • 漏掉权限与幂等:AI 默认只从功能路径出发,这两类必须显式写进要求的分类里,否则基本不会出现。
  • 一次要太多:一次喂十个接口,输出表格字段错位、ID 重复。按接口切开,逐个生成再合并。
  • 用例没有稳定 ID:没有 ID 就无法做「需求变更 → 用例增删」的追踪,也无法在缺陷单里引用;ID 一经分配不要复用。

小结:把 AI 当「用例清单的扩写器」——输入是切好的接口文档与业务规则,输出是分类齐全的表格,其中判定依据一列必须由人回查文档确认;幂等、权限与并发这三类要显式点名要求,清单定稿后再批量转测试骨架,并保留稳定的用例 ID 用于追踪。

笔记加载中…