13 KiB
技术架构概览
本 API 合规性测试框架主要由以下几个核心组件构成,它们协同工作以完成测试的定义、发现、执行和报告:
-
命令行接口 (
run_api_tests.py):- 作为测试执行的入口。
- 负责解析用户通过命令行传入的参数,例如 API 服务的基础 URL、API 规范文件路径(YAPI 或 Swagger)、测试用例目录、输出报告配置以及 LLM 相关配置。
- 初始化并驱动
APITestOrchestrator。
-
测试编排器 (
APITestOrchestrator在ddms_compliance_suite/test_orchestrator.py):- 核心控制器:是整个测试流程的指挥中心。
- 组件初始化:负责初始化和管理其他关键组件,如
InputParser(API 规范解析器)、APICaller(API 请求调用器)、TestCaseRegistry(测试用例注册表)以及可选的LLMService(大模型服务)。 - 测试流程管理:
- 调用
InputParser解析指定的 API 规范文件,获取所有端点的定义。 - 根据用户指定的过滤器(如 YAPI 分类或 Swagger 标签)筛选需要测试的 API 端点。
- 对每一个选定的 API 端点:
- 通过
TestCaseRegistry获取所有适用于该端点的自定义测试用例类。 - 实例化每个测试用例类。
- 调用
_prepare_initial_request_data方法准备初始请求数据(路径参数、查询参数、请求头、请求体)。此方法会根据全局配置和测试用例自身的配置决定是否使用 LLM 进行数据生成,并利用LLMService和动态 Pydantic 模型创建(_create_pydantic_model_from_schema)来实现。如果LLM未启用或不适用,则使用传统的基于 Schema 的数据生成逻辑(_generate_params_from_list,_generate_data_from_schema)。此阶段还实现了端点级别的LLM参数缓存。 - 依次调用测试用例实例中定义的
generate_*方法,允许测试用例修改生成的请求数据。 - 调用测试用例实例中定义的
validate_request_*方法,对即将发送的请求进行预校验。 - 使用
APICaller发送最终构建的 API 请求。 - 接收到 API 响应后,调用测试用例实例中定义的
validate_response和check_performance方法,对响应进行详细验证。
- 通过
- 调用
- 结果汇总:收集每个测试用例的执行结果 (
ExecutedTestCaseResult),汇总成每个端点的测试结果 (TestResult),并最终生成整个测试运行的摘要 (TestSummary)。
-
测试用例注册表 (
TestCaseRegistry在ddms_compliance_suite/test_case_registry.py):- 动态发现:负责在用户指定的目录 (
custom_test_cases_dir) 下扫描并动态加载所有以.py结尾的测试用例文件。 - 类识别与注册:从加载的模块中,识别出所有继承自
BaseAPITestCase的类,并根据其id属性进行注册。 - 执行顺序排序:在发现所有测试用例类后,会根据每个类的
execution_order属性(主排序键,升序)和类名__name__(次排序键,字母升序)对它们进行排序。 - 适用性筛选:提供
get_applicable_test_cases方法,根据 API 端点的 HTTP 方法和路径(通过正则表达式匹配)筛选出适用的、已排序的测试用例类列表给编排器。
- 动态发现:负责在用户指定的目录 (
-
测试框架核心 (
test_framework_core.py):BaseAPITestCase:所有自定义测试用例的基类。它定义了测试用例应具备的元数据(如id,name,description,severity,tags,execution_order,applicable_methods,applicable_paths_regex以及 LLM 使用标志位)和一系列生命周期钩子方法(如generate_*,validate_*)。APIRequestContext/APIResponseContext:数据类,分别用于封装 API 请求和响应的上下文信息,在测试用例的钩子方法间传递。ValidationResult:数据类,用于表示单个验证点的结果(通过/失败、消息、详细信息)。TestSeverity:枚举类型,定义测试用例的严重级别。
-
API 规范解析器 (
InputParser在ddms_compliance_suite/input_parser/parser.py):- 负责读取和解析 YAPI(JSON 格式)或 Swagger/OpenAPI(JSON 或 YAML 格式)的 API 规范文件。
- 将原始规范数据转换成框架内部易于处理的结构化对象(如
ParsedYAPISpec,YAPIEndpoint,ParsedSwaggerSpec,SwaggerEndpoint)。
-
API 调用器 (
APICaller在ddms_compliance_suite/api_caller/caller.py):- 封装了实际的 HTTP 请求发送逻辑。
- 接收一个
APIRequest对象(包含方法、URL、参数、头部、请求体),使用如requests库执行请求,并返回一个APIResponse对象(包含状态码、响应头、响应体内容等)。
-
LLM 服务 (
LLMService在ddms_compliance_suite/llm_utils/llm_service.py) (可选):- 如果配置了 LLM 服务(如通义千问的兼容 OpenAI 模式的 API),此组件负责与 LLM API 交互。
- 主要用于根据 Pydantic 模型(从 JSON Schema 动态创建)智能生成复杂的请求参数或请求体。
这个架构旨在提供一个灵活、可扩展的 API 测试框架,允许用户通过编写自定义的 Python 测试用例来定义复杂的验证逻辑。
自定义 APITestCase 编写指南 (更新版)
此指南帮助您创建自定义的 APITestCase 类,以扩展 DDMS 合规性验证软件的测试能力。核心理念是 代码即测试。
(您可以参考项目中的 docs/APITestCase_Development_Guide.md 文件获取更详尽的原始指南,以下内容基于该指南并加入了新特性。)
1. 创建自定义测试用例
-
创建 Python 文件:在您的自定义测试用例目录(例如
custom_testcases/)下创建一个新的.py文件。 -
继承
BaseAPITestCase:定义一个或多个类,使其继承自ddms_compliance_suite.test_framework_core.BaseAPITestCase。 -
定义元数据 (类属性):
id: str: 测试用例的全局唯一标识符 (例如"TC-MYFEATURE-001")。name: str: 人类可读的名称。description: str: 详细描述。severity: TestSeverity: 严重程度 (例如TestSeverity.CRITICAL,TestSeverity.HIGH, 等)。tags: List[str]: 分类标签 (例如["smoke", "regression"])。execution_order: int(新增): 控制测试用例的执行顺序。数值较小的会比较大的先执行。如果多个测试用例此值相同,则它们会再根据类名的字母顺序排序。默认值为100。class MyFirstCheck(BaseAPITestCase): execution_order = 10 # ... other metadata class MySecondCheck(BaseAPITestCase): execution_order = 20 # ... other metadataapplicable_methods: Optional[List[str]]: 限制适用的 HTTP 方法 (例如["POST", "PUT"])。None表示所有方法。applicable_paths_regex: Optional[str]: 限制适用的 API 路径 (Python 正则表达式)。None表示所有路径。- LLM 使用标志 (可选): 这些标志允许测试用例覆盖全局 LLM 配置。
use_llm_for_body: bool = Falseuse_llm_for_path_params: bool = Falseuse_llm_for_query_params: bool = Falseuse_llm_for_headers: bool = False(如果测试用例中不设置这些,则遵循run_api_tests.py传入的全局 LLM 开关。)
-
实现验证逻辑:重写
BaseAPITestCase中一个或多个generate_*或validate_*方法。
2. BaseAPITestCase 核心方法
-
__init__(self, endpoint_spec: Dict[str, Any], global_api_spec: Dict[str, Any]):- 构造函数。
endpoint_spec包含当前测试端点的 API 定义,global_api_spec包含完整的 API 规范。 - 基类会初始化
self.logger,可用于记录日志。
- 构造函数。
-
请求生成与修改方法: 在 API 请求发送前调用,用于修改或生成请求数据。
generate_query_params(self, current_query_params: Dict[str, Any]) -> Dict[str, Any]generate_headers(self, current_headers: Dict[str, str]) -> Dict[str, str]generate_request_body(self, current_body: Optional[Any]) -> Optional[Any]- (如果需要,您也可以尝试定义
generate_path_params方法来自定义路径参数的生成,其模式与上述类似。)
-
请求预校验方法: 在请求数据完全构建后、发送前调用,用于静态检查。返回
List[ValidationResult]。validate_request_url(self, url: str, request_context: APIRequestContext) -> List[ValidationResult]validate_request_headers(self, headers: Dict[str, str], request_context: APIRequestContext) -> List[ValidationResult]validate_request_body(self, body: Optional[Any], request_context: APIRequestContext) -> List[ValidationResult]
-
响应验证方法: 在收到 API 响应后调用,这是最主要的验证阶段。返回
List[ValidationResult]。validate_response(self, response_context: APIResponseContext, request_context: APIRequestContext) -> List[ValidationResult]- 检查状态码、响应头、响应体内容是否符合预期。
- 进行业务逻辑相关的断言。
-
性能检查方法 (可选):
check_performance(self, response_context: APIResponseContext, request_context: APIRequestContext) -> List[ValidationResult]- 通常用于检查响应时间
response_context.elapsed_time。
- 通常用于检查响应时间
3. 核心辅助类
ValidationResult(passed: bool, message: str, details: Optional[Dict[str, Any]] = None):- 封装单个验证点的结果。所有
validate_*和check_*方法都应返回此对象的列表。
- 封装单个验证点的结果。所有
APIRequestContext: 包含当前请求的详细信息(方法、URL、参数、头、体、端点规范)。APIResponseContext: 包含 API 响应的详细信息(状态码、头、JSON 内容、文本内容、耗时、原始响应对象、关联的请求上下文)。
4. 示例 (展示 execution_order)
参考您项目中的 custom_testcases/basic_checks.py,您可以像这样添加 execution_order:
# In custom_testcases/status_and_header_checks.py
from ddms_compliance_suite.test_framework_core import BaseAPITestCase, TestSeverity, ValidationResult, APIRequestContext, APIResponseContext
class StatusCodeCheck(BaseAPITestCase):
id = "TC-STATUS-001"
name = "状态码检查"
description = "验证API响应状态码。"
severity = TestSeverity.CRITICAL
tags = ["status", "smoke"]
execution_order = 10 # 希望这个检查先于下面的 HeaderCheck 执行
def validate_response(self, response_context: APIResponseContext, request_context: APIRequestContext) -> list[ValidationResult]:
results = []
if response_context.status_code == 200:
results.append(ValidationResult(passed=True, message="响应状态码为 200 OK。"))
else:
results.append(ValidationResult(passed=False, message=f"期望状态码 200,实际为 {response_context.status_code}。"))
return results
class EssentialHeaderCheck(BaseAPITestCase):
id = "TC-HEADER-ESSENTIAL-001"
name = "必要请求头 X-Trace-ID 存在性检查"
description = "验证响应中是否包含 X-Trace-ID。"
severity = TestSeverity.HIGH
tags = ["header"]
execution_order = 20 # 在状态码检查之后执行
def validate_response(self, response_context: APIResponseContext, request_context: APIRequestContext) -> list[ValidationResult]:
results = []
if "X-Trace-ID" in response_context.headers:
results.append(ValidationResult(passed=True, message="响应头中包含 X-Trace-ID。"))
else:
results.append(ValidationResult(passed=False, message="响应头中缺少 X-Trace-ID。"))
return results
5. 最佳实践
- 单一职责:让每个
APITestCase专注于特定的验证目标。 - 清晰命名:为类、ID、名称使用描述性文字。
- 善用
endpoint_spec:参考 API 定义进行精确测试。 - 详细的
ValidationResult:失败时提供充足的上下文信息。 - 日志记录:使用
self.logger记录测试过程中的重要信息和问题。
希望这份更新的架构概览和编写指南对您有所帮助!通过 execution_order,您可以更好地控制复杂场景下测试用例的执行流程。