107 lines
3.6 KiB
Markdown
107 lines
3.6 KiB
Markdown
|
|
---
|
|||
|
|
name: Verify统一断言改造
|
|||
|
|
overview: 在 Api 上新增 Resp() 方法让 Verify 回调内可以同时做响应值断言和数据库校验,然后更新文档将统一的 Verify 模式作为推荐范式。
|
|||
|
|
todos:
|
|||
|
|
- id: add-resp-method
|
|||
|
|
content: "testing_api.go: Api 新增 lastResp 字段 + Resp() 方法,execute 中调 verifyFn 前设置 lastResp"
|
|||
|
|
status: completed
|
|||
|
|
- id: update-doc
|
|||
|
|
content: "docs/Testing_API测试框架.md: 将 Verify 统一断言作为推荐范式,resp 外部断言降为兼容用法"
|
|||
|
|
status: completed
|
|||
|
|
isProject: false
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Verify 统一断言改造
|
|||
|
|
|
|||
|
|
## 问题
|
|||
|
|
|
|||
|
|
当前 `Verify(func(a *Api) error)` 的回调只能通过 `a.DB()` 做数据库校验,无法访问响应体。导致值断言必须在外部用 `resp.GetBody()` + `resp.Fail()` 完成,逻辑分散在两处。
|
|||
|
|
|
|||
|
|
## 方案:Api 上新增 Resp() 方法
|
|||
|
|
|
|||
|
|
在 `execute` 调用 `verifyFn` 之前,把 `resp` 存到 `Api` 上,然后通过 `a.Resp()` 暴露给回调。
|
|||
|
|
|
|||
|
|
### 代码改动([testing_api.go](testing_api.go))
|
|||
|
|
|
|||
|
|
**1. Api 结构体新增字段**(第 46-51 行附近):
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
type Api struct {
|
|||
|
|
app *TestApp
|
|||
|
|
path string
|
|||
|
|
t *testing.T
|
|||
|
|
session Map
|
|||
|
|
lastResp *ApiResponse // 新增:最近一次请求的响应
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**2. 新增 Resp() 方法**:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// Resp 获取最近一次请求的响应(在 Verify 回调中使用)
|
|||
|
|
func (a *Api) Resp() *ApiResponse {
|
|||
|
|
return a.lastResp
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**3. execute 中调用 verifyFn 前设置 lastResp**(第 296 行附近):
|
|||
|
|
|
|||
|
|
在 `if passed && c.verifyFn != nil` 之前加一行:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
c.api.lastResp = resp
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**4. WithSession 中传递 lastResp**(可选,因为 WithSession 创建新实例时 lastResp 默认 nil 即可,Verify 内不会跨实例调用)。
|
|||
|
|
|
|||
|
|
### 改动影响
|
|||
|
|
|
|||
|
|
- Verify 的签名 `func(a *Api) error` 不变,完全向后兼容
|
|||
|
|
- 现有 Verify 回调代码无需修改
|
|||
|
|
- 新的 Verify 回调可以用 `a.Resp()` 访问响应体做值断言
|
|||
|
|
- 外部 `resp.GetBody()` + `resp.Fail()` 仍然可用,作为兼容方式保留
|
|||
|
|
|
|||
|
|
### 文档改动([docs/Testing_API测试框架.md](docs/Testing_API测试框架.md))
|
|||
|
|
|
|||
|
|
将"用例编写范式"中的推荐模式从"Verify 做 DB 校验 + 外部 resp 做值断言"改为**统一在 Verify 内完成**:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
resp := a.WithSession(Map{"user_id": int64(1)}).
|
|||
|
|
JSON(Map{"goods_id": goodsId, "quantity": 3, "address": "XX路1号"}).
|
|||
|
|
Verify(func(a *Api) error {
|
|||
|
|
// 响应值断言
|
|||
|
|
result := a.Resp().GetBody().GetMap("result")
|
|||
|
|
if result.GetCeilInt64("id") <= 0 {
|
|||
|
|
return fmt.Errorf("订单 ID 必须大于 0")
|
|||
|
|
}
|
|||
|
|
if result.GetString("sn") == "" {
|
|||
|
|
return fmt.Errorf("订单编号 sn 不能为空")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 数据库状态校验
|
|||
|
|
row := a.DB().Get("order", "*", Map{"AND": Map{
|
|||
|
|
"user_id": int64(1), "goods_id": goodsId,
|
|||
|
|
}})
|
|||
|
|
if row == nil {
|
|||
|
|
return fmt.Errorf("order 表未写入订单记录")
|
|||
|
|
}
|
|||
|
|
if row.GetCeilInt64("state") != 0 {
|
|||
|
|
return fmt.Errorf("新订单 state 期望 0, 实际 %d", row.GetCeilInt64("state"))
|
|||
|
|
}
|
|||
|
|
// ...
|
|||
|
|
return nil
|
|||
|
|
}).
|
|||
|
|
Post("正常创建订单", 0, Map{
|
|||
|
|
"id": int64(1), "sn": "sample", "total_price": float64(1.0),
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
文档中需要调整的位置:
|
|||
|
|
|
|||
|
|
- 快速开始的完整范例
|
|||
|
|
- 用例编写范式的流程图和表格
|
|||
|
|
- "正确请求与响应断言"小节(合并为"正确请求与 Verify 校验")
|
|||
|
|
- "不好的写法 vs 推荐的写法"对比
|
|||
|
|
- 保留 `resp.GetBody()` + `resp.Fail()` 的说明但标注为兼容用法
|
|||
|
|
|