feat(api): 增强 API 测试框架功能与文档
- 在 Api 结构中新增 lastResp 字段以存储最近请求的响应 - 添加 Verify 方法,支持自定义校验函数并返回 ApiCase 构建器 - 新增 Resp 方法,获取最近一次请求的响应以便于断言 - 在 TestCollector 中添加 Visited 字段,记录已调用的路径 - 更新 GenerateSwagger 方法,支持部分运行时保留未运行端点的已有数据 - 完善文档,增加用例编写范式和示例,提升测试框架的可用性与易用性
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user