diff --git a/docs/Testing_API测试框架.md b/docs/Testing_API测试框架.md index 31e93ff..1ef7a37 100644 --- a/docs/Testing_API测试框架.md +++ b/docs/Testing_API测试框架.md @@ -1,4 +1,4 @@ -# HoTime API 测试框架使用说明 +# HoTime API 测试框架使用说明 不启动 HTTP 服务、自动事务回滚、链式 API、覆盖率报告、API 调试控制台生成。 @@ -209,6 +209,7 @@ func (app *TestApp) DB() *HoTimeDB | `a.Query(params)` | 设置 URL 查询参数 | `*ApiCase` | | `a.Form(body)` | 设置 form 表单 body | `*ApiCase` | | `a.File(field, name, content)` | 设置上传文件 | `*ApiCase` | +| `a.Note(note)` | 为下一条用例设置备注,备注显示在调试控制台 | `*ApiCase` | | `a.WithSession(s)` | 返回携带 session 的新 Api | `*Api` | | `a.Get(desc, status, msg...)` | 直接 GET 请求(无数据场景) | `*ApiResponse` | | `a.Post(desc, status, msg...)` | 直接 POST 请求(无数据场景) | `*ApiResponse` | @@ -226,6 +227,7 @@ func (app *TestApp) DB() *HoTimeDB | `c.Query(params)` | 追加 URL 查询参数 | `*ApiCase` | | `c.Form(body)` | 追加 form 字段 | `*ApiCase` | | `c.File(field, name, content)` | 设置上传文件 | `*ApiCase` | +| `c.Note(note)` | 为本条用例添加可选备注,备注显示在调试控制台用例详情中 | `*ApiCase` | | `c.WithSession(s)` | 覆盖本次请求的 session | `*ApiCase` | | `c.Get(desc, status, msg...)` | 终端:发送 GET 请求并断言 | `*ApiResponse` | | `c.Post(desc, status, msg...)` | 终端:发送 POST 请求并断言 | `*ApiResponse` | @@ -303,6 +305,10 @@ a.JSON(Map{"name": "新名字"}).Put("更新", 0) // DELETE + URL 参数 a.Query(Map{"id": "5"}).Delete("删除", 0) +// 带备注的用例(备注显示在调试控制台用例详情中,没有备注时不显示) +a.Form(Map{"phone": "138", "code": "1234"}).Note("sign = MD5(smsProxyKey+timestamp),smsProxyKey 见 config.json").Post("正常发送", 0) +a.Note("需先登录后在 Header 中携带 Authorization token").JSON(Map{"name": "新名字"}).Post("更新信息", 0) + // 校验响应中的具体字段 resp := a.JSON(Map{"name": "138", "password": "123"}).Post("正常登录", 0) if resp.GetBody().GetMap("result").GetString("token") == "" { @@ -408,6 +414,7 @@ func TestMain(m *testing.M) { "name": "正常登录", "method": "POST", "passed": true, + "note": "备注内容(可选,未调用 .Note() 则不出现该字段)", "query": {}, "json": { "name": "13800138000", "password": "123456" }, "response": { "status": 0, "msg": "ok", "data": {} } @@ -434,6 +441,7 @@ func TestMain(m *testing.M) { | `params[].in` | string | 参数位置:`query` / `form` / `json` | | `cases` | array | 实际运行的测试用例列表;若 `tested=false` 则为空数组 | | `cases[].passed` | bool | 该用例是否通过(断言的 status/message 均匹配) | +| `cases[].note` | string | 用例备注(可选),由 `.Note("...")` 设置,为空时不输出该字段 | | `cases[].query` | object | 该用例的 Query 参数(GET 场景) | | `cases[].json` | object | 该用例的 JSON Body(`a.JSON(...)` 场景) | | `cases[].form` | object | 该用例的 Form 参数(`a.Form(...)` 场景) | @@ -490,7 +498,7 @@ testApp.GenerateSwagger( | **三级导航** | 左侧侧边栏按 项目 > 控制器 > 方法 三级树形展示(一级默认展开,二级默认收起) | | **搜索和筛选** | 支持关键字搜索,按 全部/已测试/未测试/通过/未通过 五种模式筛选 | | **左右分栏** | 接口信息栏右侧显示用例数量(`用例: N`);左侧为请求构建器,右侧为结果面板(两 tab 切换) | -| **测试用例 tab** | 右侧默认展示"测试用例"tab,手风琴列表(第一个默认展开);必填参数前显示红色 `*` 标记 | +| **测试用例 tab** | 右侧默认展示"测试用例"tab,手风琴列表(第一个默认展开);必填参数前显示红色 `*` 标记;用例设置了 `.Note(...)` 时在展开体和选中面板顶部显示灰色备注文字 | | **发送结果 tab** | 点击"发送"后自动切换到"发送结果"tab 展示 cURL 命令和响应内容;可随时手动切回 | | **必填参数推断** | 从测试用例自动推断必填参数(有 status==3 且值为空的用例),调试面板和用例展示均标记 `*` | | **预填用例** | 默认选中第一个用例自动填充左侧所有参数;"手动填写"置于列表末尾;必填字段有 `*` 标记 | diff --git a/testing_api.go b/testing_api.go index 886cd14..9ef69d9 100644 --- a/testing_api.go +++ b/testing_api.go @@ -110,6 +110,13 @@ func (a *Api) Delete(desc string, status int, msg ...string) *ApiResponse { return a.newCase().Delete(desc, status, msg...) } +// Note 为下一条用例设置备注,返回可继续链式的 ApiCase +func (a *Api) Note(note string) *ApiCase { + c := a.newCase() + c.note = note + return c +} + // DB 获取数据库实例(在测试事务内) func (a *Api) DB() *HoTimeDB { return &a.app.Db @@ -134,6 +141,7 @@ type ApiCase struct { fileField string fileName string fileContent []byte + note string } // JSON 叠加 JSON body @@ -180,6 +188,12 @@ func (c *ApiCase) WithSession(s Map) *ApiCase { return c } +// Note 为本条用例添加可选备注,备注将显示在 swagger 调试控制台 +func (c *ApiCase) Note(note string) *ApiCase { + c.note = note + return c +} + // Get 终端操作:GET 请求 + 断言 func (c *ApiCase) Get(desc string, status int, msg ...string) *ApiResponse { return c.execute("GET", desc, status, msg...) @@ -247,6 +261,7 @@ func (c *ApiCase) execute(method, desc string, expectStatus int, expectMsg ...st Passed: passed, Duration: duration, Method: method, + Note: c.note, } if c.query != nil { record.Query = c.query diff --git a/testing_helper.go b/testing_helper.go index cd4703a..b8827c7 100644 --- a/testing_helper.go +++ b/testing_helper.go @@ -46,18 +46,19 @@ type MethodCoverage struct { // TestRecord 单条测试记录 type TestRecord struct { - Path string - Desc string - CaseName string - Passed bool - Duration time.Duration - Method string - Query map[string]interface{} - JsonBody interface{} - FormBody map[string]interface{} - HasFile bool - FileField string + Path string + Desc string + CaseName string + Passed bool + Duration time.Duration + Method string + Query map[string]interface{} + JsonBody interface{} + FormBody map[string]interface{} + HasFile bool + FileField string ResponseBody map[string]interface{} + Note string } // TestCollector 线程安全的测试记录收集器 diff --git a/testing_swagger.go b/testing_swagger.go index 0410920..d4319b8 100644 --- a/testing_swagger.go +++ b/testing_swagger.go @@ -13,6 +13,7 @@ import ( type swaggerTestCase struct { Name string `json:"name"` + Note string `json:"note,omitempty"` Method string `json:"method"` Passed bool `json:"passed"` Query map[string]interface{} `json:"query,omitempty"` @@ -227,10 +228,10 @@ func (that *TestApp) GenerateSwagger(title, version, outputDir string) error { var cases []swaggerTestCase for _, r := range recs { - tc := swaggerTestCase{ - Name: r.CaseName, Method: r.Method, Passed: r.Passed, - Response: r.ResponseBody, - } + tc := swaggerTestCase{ + Name: r.CaseName, Note: r.Note, Method: r.Method, Passed: r.Passed, + Response: r.ResponseBody, + } if r.Query != nil { tc.Query = r.Query } @@ -502,7 +503,7 @@ function show(){ h+='