说明
在生成用例之前,需要将OpenAPI 3.0.1 的文件解析为对应的Api和Parameter模型
解析
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.snycedu.platform.test.model.Api;
import com.snycedu.platform.test.model.Parameter;
import java.io.File;
import java.math.BigDecimal;
import java.util.*;
public class OpenApiParser {
public static List<Api> parseOpenApi(String yamlFilePath) throws Exception {
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
Map<String, Object> openApiMap = mapper.readValue(new File(yamlFilePath), Map.class);
// 全局组件(用于解析$ref)
Map<String, Object> components = (Map<String, Object>) openApiMap.get("components");
Map<String, Object> schemas = components != null ? (Map<String, Object>) components.get("schemas") : new HashMap<>();
Map<String, Object> parameters = components != null ? (Map<String, Object>) components.get("parameters") : new HashMap<>();
List<Api> apiList = new ArrayList<>();
// 遍历所有路径
Map<String, Object> paths = (Map<String, Object>) openApiMap.get("paths");
for (String apiPath : paths.keySet()) {
Map<String, Object> pathItem = (Map<String, Object>) paths.get(apiPath);
// 遍历所有HTTP方法(get/post/put等)
for (String method : pathItem.keySet()) {
if (method.startsWith("x-")) continue; // 跳过扩展字段
Map<String, Object> operation = (Map<String, Object>) pathItem.get(method);
Api api = new Api();
api.setApi(apiPath);
api.setMethod(method.toUpperCase());
api.setPath(new ArrayList<>());
api.setQuery(new ArrayList<>());
api.setBody(new ArrayList<>());
// ----------------- 1. 解析路径参数 -----------------
// 从URL路径中提取显式声明的路径参数(如 /api/{id})
extractPathParamsFromUrl(apiPath, api.getPath());
// 解析OpenAPI定义的parameters中的路径参数(覆盖自动提取的值)
parseOperationParameters(operation, api, "path", parameters, schemas);
// ----------------- 2. 解析查询参数 -----------------
parseOperationParameters(operation, api, "query", parameters, schemas);
// ----------------- 3. 解析请求体 -----------------
Map<String, Object> requestBody = (Map<String, Object>) operation.get("requestBody");
if (requestBody != null) {
Map<String, Object> content = (Map<String, Object>) ((Map<String, Object>) requestBody.get("content")).values().iterator().next();
Map<String, Object> schema = (Map<String, Object>) content.get("schema");
List<Parameter> bodyParams = parseSchema(schema, schemas, "#/components/schemas/", "");
api.getBody().addAll(bodyParams);
}
apiList.add(api);
}
}
return apiList;
}
// 从URL路径(如 /users/{id}/detail)提取路径参数
private static void extractPathParamsFromUrl(String apiPath, List<Parameter> pathParams) {
String[] segments = apiPath.split("/");
for (String segment : segments) {
if (segment.startsWith("{") && segment.endsWith("}")) {
String paramName = segment.substring(1, segment.length() - 1);
Parameter param = new Parameter();
param.setName(paramName);
param.setLocation(paramName);
param.setParamType("string"); // 默认类型,实际类型由parameters定义覆盖
param.setRequired(true); // 路径参数必填
pathParams.add(param);
}
}
}
// 解析操作中的参数(path/query/header等)
private static void parseOperationParameters(
Map<String, Object> operation,
Api api,
String paramIn,
Map<String, Object> globalParameters,
Map<String, Object> schemas
) {
List<Map<String, Object>> parameters = (List<Map<String, Object>>) operation.get("parameters");
if (parameters == null) return;
for (Map<String, Object> paramDef : parameters) {
// 处理参数引用(如 $ref: '#/components/parameters/UserId')
if (paramDef.containsKey("$ref")) {
String ref = ((String) paramDef.get("$ref")).substring("#/components/parameters/".length());
paramDef = (Map<String, Object>) globalParameters.get(ref);
}
String inType = (String) paramDef.get("in");
if (!paramIn.equals(inType)) continue;
Parameter param = new Parameter();
param.setName((String) paramDef.get("name"));
param.setLocation(param.getName());
param.setRequired(Boolean.TRUE.equals(paramDef.get("required")));
// 解析参数类型和约束
Map<String, Object> schema = (Map<String, Object>) paramDef.get("schema");
if (schema != null) {
param.setParamType((String) schema.get("type"));
applySchemaConstraints(param, schema);
// 处理枚举
if (schema.containsKey("enum")) {
param.setEnums((List<?>) schema.get("enum"));
}
// 处理嵌套对象/数组
if ("object".equals(param.getParamType()) || "array".equals(param.getParamType())) {
param.setRefBody(parseSchema(schema, schemas, "#/components/schemas/", ""));
}
}
// 添加到对应的参数列表
if ("path".equals(inType)) {
api.getPath().add(param);
} else if ("query".equals(inType)) {
api.getQuery().add(param);
}
}
}
private static List<Parameter> parseSchema(Map<String, Object> schema,
Map<String, Object> schemas,
String refPrefix,
String parentPath) { // 新增parentPath参数
List<Parameter> parameters = new ArrayList<>();
// 处理$ref引用
if (schema.containsKey("$ref")) {
String ref = ((String) schema.get("$ref")).substring(refPrefix.length());
Map<String, Object> refSchema = (Map<String, Object>) schemas.get(ref);
return parseSchema(refSchema, schemas, refPrefix, parentPath); // 传递当前父路径
}
String type = (String) schema.get("type");
List<String> required = (List<String>) schema.get("required");
if ("object".equals(type)) {
Map<String, Object> properties = (Map<String, Object>) schema.get("properties");
for (String propName : properties.keySet()) {
// 构建当前参数的完整路径
String currentPath = parentPath.isEmpty() ? propName : parentPath + "." + propName;
Map<String, Object> propSchema = (Map<String, Object>) properties.get(propName);
Parameter param = createParameter(propName, propSchema, schemas, required, refPrefix, currentPath);
parameters.add(param);
}
}
return parameters;
}
private static Parameter createParameter(String name,
Map<String, Object> schema,
Map<String, Object> schemas,
List<String> required,
String refPrefix,
String currentPath) { // 新增currentPath参数
Parameter param = new Parameter();
param.setName(name);
param.setLocation(currentPath); // 直接使用构建好的路径
String type = (String) schema.get("type");
param.setParamType(type);
param.setRequired(required != null && required.contains(name));
applySchemaConstraints(param, schema);
// 处理嵌套结构(递归调用时传递当前路径)
if ("object".equals(type)) {
param.setRefBody(parseSchema(schema, schemas, refPrefix, currentPath)); // 传递当前路径
} else if ("array".equals(type)) {
Map<String, Object> items = (Map<String, Object>) schema.get("items");
param.setRefBody(parseSchema(items, schemas, refPrefix, currentPath)); // 传递当前路径
} else if (schema.containsKey("$ref")) {
String ref = ((String) schema.get("$ref")).substring(refPrefix.length());
Map<String, Object> refSchema = (Map<String, Object>) schemas.get(ref);
param.setParamType("object");
param.setRefBody(parseSchema(refSchema, schemas, refPrefix, currentPath)); // 传递当前路径
}
return param;
}
// 应用Schema约束(最大值、正则等)
private static void applySchemaConstraints(Parameter param, Map<String, Object> schema) {
if (schema.containsKey("maximum")) {
param.setMaximum(new BigDecimal(schema.get("maximum").toString()));
}
if (schema.containsKey("minimum")) {
param.setMinimum(new BigDecimal(schema.get("minimum").toString()));
}
if (schema.containsKey("maxLength")) {
param.setMaxLength((int) schema.get("maxLength"));
}
if (schema.containsKey("minLength")) {
param.setMinLength((int) schema.get("minLength"));
}
if (schema.containsKey("pattern")) {
param.setPattern((String) schema.get("pattern"));
}
if(schema.containsKey("enum")){
param.setEnums((List<?>) schema.get("enum"));
}
}
}
编写测试
package com.snycedu.platform.test;
import com.alibaba.fastjson.JSON;
import com.snycedu.platform.test.model.Api;
import com.snycedu.platform.test.model.TestCaseScenario;
import com.snycedu.platform.test.parse.OpenApiParser;
import java.util.List;
public class Test {
public static void main(String[] args) throws Exception {
// 构造基础请求负载
List<Api> apiList = OpenApiParser.parseOpenApi("/Users/xiyubaby.17/Desktop/04workspace/testtools/src/main/resources/article-tmp.yaml");
System.out.println(JSON.toJSONString(apiList));
}
}
输出测试结果
[{
"api": "/api/v1/article",
"body": [{
"location": "title",
"maxLength": 32,
"minLength": 10,
"name": "title",
"paramType": "string",
"required": true
}, {
"enums": ["CSDN", "HEAD_NEWS"],
"location": "type",
"name": "type",
"paramType": "string",
"required": true
}, {
"location": "content",
"name": "content",
"paramType": "object",
"refBody": [{
"location": "content.id",
"maximum": 99999999,
"minimum": 100,
"name": "id",
"paramType": "integer",
"required": false
}, {
"enums": ["MARKDOWN", "TEXT"],
"location": "content.format",
"name": "format",
"paramType": "string",
"required": false
}],
"required": true
}],
"method": "POST",
"path": [],
"query": []
}]