feat(api): 增强 API 测试框架功能与文档

- 在 Api 结构中新增 lastResp 字段以存储最近请求的响应
- 添加 Verify 方法,支持自定义校验函数并返回 ApiCase 构建器
- 新增 Resp 方法,获取最近一次请求的响应以便于断言
- 在 TestCollector 中添加 Visited 字段,记录已调用的路径
- 更新 GenerateSwagger 方法,支持部分运行时保留未运行端点的已有数据
- 完善文档,增加用例编写范式和示例,提升测试框架的可用性与易用性
This commit is contained in:
2026-03-30 01:55:07 +08:00
parent 3681564ca4
commit 37a67f5810
14 changed files with 2665 additions and 639 deletions
+66
View File
@@ -193,6 +193,10 @@ func (that *TestApp) GenerateSwagger(title, version, outputDir string) error {
that.collector.mu.Lock()
records := make([]TestRecord, len(that.collector.Records))
copy(records, that.collector.Records)
visited := map[string]bool{}
for k, v := range that.collector.Visited {
visited[k] = v
}
that.collector.mu.Unlock()
pathRecords := map[string][]TestRecord{}
@@ -206,6 +210,25 @@ func (that *TestApp) GenerateSwagger(title, version, outputDir string) error {
return fmt.Errorf("创建模块目录 %s 失败: %w", projName, err)
}
// 判断是否为部分运行(-run 过滤):统计本项目下所有端点是否全部被访问
totalPaths := 0
visitedPaths := 0
for ctrName, ctr := range projDef.Proj {
for methodName := range ctr {
totalPaths++
if visited["/"+projName+"/"+ctrName+"/"+methodName] {
visitedPaths++
}
}
}
isPartialRun := visitedPaths > 0 && visitedPaths < totalPaths
// 仅部分运行时才加载已有 spec 用于合并,全量运行时清空重建
var existingEndpoints map[string]map[string]interface{}
if isPartialRun {
existingEndpoints = loadExistingEndpoints(filepath.Join(moduleDir, "api-spec.json"))
}
endpoints := []map[string]interface{}{}
for ctrName, ctr := range projDef.Proj {
for methodName := range ctr {
@@ -223,9 +246,24 @@ func (that *TestApp) GenerateSwagger(title, version, outputDir string) error {
}
recs := pathRecords[apiPath]
// 部分运行且该路径未被访问 → 保留已有 spec 中的数据
if isPartialRun && !visited[apiPath] {
if existing, ok := existingEndpoints[apiPath]; ok {
endpoints = append(endpoints, existing)
continue
}
}
httpMethod := "POST"
if len(recs) > 0 && recs[0].Method != "" {
httpMethod = strings.ToUpper(recs[0].Method)
} else if isPartialRun {
if existing, ok := existingEndpoints[apiPath]; ok {
if m, ok := existing["method"].(string); ok && m != "" {
httpMethod = m
}
}
}
var cases []swaggerTestCase
@@ -292,6 +330,34 @@ func (that *TestApp) GenerateSwagger(title, version, outputDir string) error {
return nil
}
// loadExistingEndpoints 读取已有的 api-spec.json,返回 path → endpoint 映射。
// 用于 -run 部分运行时保留未运行端点的已有数据,避免覆盖清空。
func loadExistingEndpoints(specPath string) map[string]map[string]interface{} {
result := map[string]map[string]interface{}{}
data, err := os.ReadFile(specPath)
if err != nil {
return result
}
var spec map[string]interface{}
if err := json.Unmarshal(data, &spec); err != nil {
return result
}
eps, ok := spec["endpoints"].([]interface{})
if !ok {
return result
}
for _, raw := range eps {
ep, ok := raw.(map[string]interface{})
if !ok {
continue
}
if path, ok := ep["path"].(string); ok {
result[path] = ep
}
}
return result
}
func generateSwaggerPortal(swaggerRoot string) error {
entries, err := os.ReadDir(swaggerRoot)
if err != nil {