feat(api): 增强 API 测试框架的断言功能

- 修改 ApiCase 和 ApiResponse 结构,新增 Verify 方法以支持自定义校验函数
- 更新 Get、Post、Put、Delete 方法,允许使用 expect 参数进行更灵活的响应断言
- 添加 ExpectResult 方法,支持对响应结果的结构和类型进行后置校验
- 更新文档,详细说明新功能的使用方法和示例,提升测试框架的可用性
- 增强 Swagger 生成逻辑,支持输出新的 expect 和 verifyError 字段
This commit is contained in:
2026-03-21 13:38:22 +08:00
parent 9c5a7630a4
commit 355d19418b
10 changed files with 1263 additions and 182 deletions
+5 -4
View File
@@ -8,10 +8,11 @@ import (
)
var ProjectTest = ProjTest{
"test": TestTest,
"mysql": MysqlTest,
"dmdb": DmdbTest,
"cache": CacheTest,
"test": TestTest,
"mysql": MysqlTest,
"dmdb": DmdbTest,
"cache": CacheTest,
"expect_demo": ExpectDemoTest,
}
var testApp *TestApp
+81
View File
@@ -0,0 +1,81 @@
package app
import (
"fmt"
. "code.hoteas.com/golang/hotime"
. "code.hoteas.com/golang/hotime/common"
)
var ExpectDemoTest = CtrTest{
"string_result": {Desc: "返回字符串", Func: func(a *Api) {
a.Get("result是字符串", 0, "操作成功")
}},
"number_result": {Desc: "返回整数", Func: func(a *Api) {
a.Get("result是整数", 0, int64(1))
}},
"float_result": {Desc: "返回小数", Func: func(a *Api) {
a.Get("result是小数", 0, float64(1.0))
}},
"bool_result": {Desc: "返回布尔", Func: func(a *Api) {
a.Get("result是布尔", 0, true)
}},
"map_result": {Desc: "返回对象", Func: func(a *Api) {
a.Get("result是Map-校验字段名和类型", 0, Map{
"id": int64(1), "name": "sample", "price": float64(1.0), "in_stock": true,
})
}},
"nested_result": {Desc: "返回嵌套对象", Func: func(a *Api) {
a.Get("深层嵌套Map+Slice+Map", 0, Map{
"id": int64(1), "title": "sample",
"customer": Map{"id": int64(1), "name": "sample", "phone": "sample"},
"items": Slice{Map{
"id": int64(1), "goods_name": "sample", "quantity": int64(1), "price": float64(1.0),
}},
"extra": Map{
"delivery": Map{"address": "sample", "fee": int64(1)},
},
})
}},
"slice_result": {Desc: "返回数组", Func: func(a *Api) {
a.Get("result直接是Slice", 0, Slice{Map{"id": int64(1), "name": "sample"}})
}},
"error_demo": {Desc: "错误+成功混合", Func: func(a *Api) {
a.Form(Map{"name": ""}).Post("名称为空-错误断言", 3, "名称不能为空")
a.Form(Map{"name": "测试"}).Post("名称正确-结构校验", 0, Map{
"id": int64(1), "name": "sample", "created": true,
})
}},
"no_expect": {Desc: "不传预期(仅校验status)", Func: func(a *Api) {
a.Get("只校验status=0", 0)
}},
"verify_demo": {Desc: "Verify数据库校验", Func: func(a *Api) {
a.Form(Map{"name": ""}).Post("名称为空", 3, "名称不能为空")
a.Form(Map{"name": "verify_test"}).
Verify(func(a *Api) error {
row := a.DB().Get("test_batch", "*", Map{"AND": Map{"name": "verify_test", "title": "verify_demo"}})
if row == nil {
return fmt.Errorf("test_batch 表未找到 name=verify_test 的记录")
}
if row.GetCeilInt64("state") != 0 {
return fmt.Errorf("test_batch.state 期望 0, 实际 %d", row.GetCeilInt64("state"))
}
return nil
}).
Post("写入并校验DB-正确", 0, Map{"id": int64(1), "name": "sample"})
a.Form(Map{"name": "verify_fail"}).
Verify(func(a *Api) error {
row := a.DB().Get("test_batch", "*", Map{"AND": Map{"name": "verify_fail", "title": "verify_demo"}})
if row == nil {
return fmt.Errorf("test_batch 表未找到 name=verify_fail 的记录")
}
if row.GetCeilInt64("state") != 99 {
return fmt.Errorf("test_batch.state 期望 99, 实际 %d(接口写入的是0,这里故意校验99来演示失败效果)", row.GetCeilInt64("state"))
}
return nil
}).
Post("写入并校验DB-故意失败", 0, Map{"id": int64(1), "name": "sample"})
}},
}
+5 -4
View File
@@ -5,8 +5,9 @@ import (
)
var Project = Proj{
"test": TestCtr,
"mysql": MysqlCtr,
"dmdb": DmdbCtr,
"cache": CacheCtr,
"test": TestCtr,
"mysql": MysqlCtr,
"dmdb": DmdbCtr,
"cache": CacheCtr,
"expect_demo": ExpectDemoCtr,
}
+61
View File
@@ -43,6 +43,67 @@ func nowFunc(that *Context) string {
return "NOW()"
}
// ExpectDemoCtr 用于展示 result 结构校验的各种场景
var ExpectDemoCtr = Ctr{
"string_result": func(that *Context) {
that.Display(0, "操作成功")
},
"number_result": func(that *Context) {
that.Display(0, 42)
},
"float_result": func(that *Context) {
that.Display(0, 3.14)
},
"bool_result": func(that *Context) {
that.Display(0, true)
},
"map_result": func(that *Context) {
that.Display(0, Map{
"id": 1, "name": "示例商品", "price": 19.9, "in_stock": true,
})
},
"nested_result": func(that *Context) {
that.Display(0, Map{
"id": 100, "title": "测试订单",
"customer": Map{"id": 1, "name": "张三", "phone": "13800138000"},
"items": Slice{
Map{"id": 1, "goods_name": "苹果", "quantity": 5, "price": 3.5},
Map{"id": 2, "goods_name": "香蕉", "quantity": 10, "price": 2.0},
},
"extra": Map{
"delivery": Map{"address": "XX路1号", "fee": 5},
},
})
},
"slice_result": func(that *Context) {
that.Display(0, Slice{
Map{"id": 1, "name": "标签A"},
Map{"id": 2, "name": "标签B"},
})
},
"error_demo": func(that *Context) {
name := that.Req.FormValue("name")
if name == "" {
that.Display(3, "名称不能为空")
return
}
that.Display(0, Map{"id": 1, "name": name, "created": true})
},
"no_expect": func(that *Context) {
that.Display(0, Map{"id": 1, "data": "无预期校验"})
},
"verify_demo": func(that *Context) {
name := that.Req.FormValue("name")
if name == "" {
that.Display(3, "名称不能为空")
return
}
initTestTables(that)
id := that.Db.Insert("test_batch", Map{"name": name, "title": "verify_demo", "state": 0})
that.Display(0, Map{"id": id, "name": name})
},
}
// TestCtr 通用 ORM 测试控制器(数据库无关)
var TestCtr = Ctr{
"test": func(that *Context) {