compliance/Framework_And_TestCase_Guide.md
2025-05-26 15:38:37 +08:00

172 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 技术架构概览
本 API 合规性测试框架主要由以下几个核心组件构成,它们协同工作以完成测试的定义、发现、执行和报告:
1. **命令行接口 (`run_api_tests.py`)**:
* 作为测试执行的入口。
* 负责解析用户通过命令行传入的参数,例如 API 服务的基础 URL、API 规范文件路径YAPI 或 Swagger、测试用例目录、输出报告配置以及 LLM 相关配置。
* 初始化并驱动 `APITestOrchestrator`
2. **测试编排器 (`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`)。
3. **测试用例注册表 (`TestCaseRegistry` 在 `ddms_compliance_suite/test_case_registry.py`)**:
* **动态发现**:负责在用户指定的目录 (`custom_test_cases_dir`) 下扫描并动态加载所有以 `.py` 结尾的测试用例文件。
* **类识别与注册**:从加载的模块中,识别出所有继承自 `BaseAPITestCase` 的类,并根据其 `id` 属性进行注册。
* **执行顺序排序**:在发现所有测试用例类后,会根据每个类的 `execution_order` 属性(主排序键,升序)和类名 `__name__`(次排序键,字母升序)对它们进行排序。
* **适用性筛选**:提供 `get_applicable_test_cases` 方法,根据 API 端点的 HTTP 方法和路径(通过正则表达式匹配)筛选出适用的、已排序的测试用例类列表给编排器。
4. **测试框架核心 (`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`**:枚举类型,定义测试用例的严重级别。
5. **API 规范解析器 (`InputParser` 在 `ddms_compliance_suite/input_parser/parser.py`)**:
* 负责读取和解析 YAPIJSON 格式)或 Swagger/OpenAPIJSON 或 YAML 格式)的 API 规范文件。
* 将原始规范数据转换成框架内部易于处理的结构化对象(如 `ParsedYAPISpec`, `YAPIEndpoint`, `ParsedSwaggerSpec`, `SwaggerEndpoint`)。
6. **API 调用器 (`APICaller` 在 `ddms_compliance_suite/api_caller/caller.py`)**:
* 封装了实际的 HTTP 请求发送逻辑。
* 接收一个 `APIRequest` 对象包含方法、URL、参数、头部、请求体使用如 `requests` 库执行请求,并返回一个 `APIResponse` 对象(包含状态码、响应头、响应体内容等)。
7. **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. 创建自定义测试用例
1. **创建 Python 文件**:在您的自定义测试用例目录(例如 `custom_testcases/`)下创建一个新的 `.py` 文件。
2. **继承 `BaseAPITestCase`**:定义一个或多个类,使其继承自 `ddms_compliance_suite.test_framework_core.BaseAPITestCase`
3. **定义元数据 (类属性)**
* `id: str`: 测试用例的全局唯一标识符 (例如 `"TC-MYFEATURE-001"`)。
* `name: str`: 人类可读的名称。
* `description: str`: 详细描述。
* `severity: TestSeverity`: 严重程度 (例如 `TestSeverity.CRITICAL`, `TestSeverity.HIGH`, 等)。
* `tags: List[str]`: 分类标签 (例如 `["smoke", "regression"]`)。
* **`execution_order: int` (新增)**: 控制测试用例的执行顺序。**数值较小的会比较大的先执行**。如果多个测试用例此值相同,则它们会再根据类名的字母顺序排序。默认值为 `100`
```python
class MyFirstCheck(BaseAPITestCase):
execution_order = 10
# ... other metadata
class MySecondCheck(BaseAPITestCase):
execution_order = 20
# ... other metadata
```
* `applicable_methods: Optional[List[str]]`: 限制适用的 HTTP 方法 (例如 `["POST", "PUT"]`)。`None` 表示所有方法。
* `applicable_paths_regex: Optional[str]`: 限制适用的 API 路径 (Python 正则表达式)。`None` 表示所有路径。
* **LLM 使用标志 (可选)**: 这些标志允许测试用例覆盖全局 LLM 配置。
* `use_llm_for_body: bool = False`
* `use_llm_for_path_params: bool = False`
* `use_llm_for_query_params: bool = False`
* `use_llm_for_headers: bool = False`
(如果测试用例中不设置这些,则遵循 `run_api_tests.py` 传入的全局 LLM 开关。)
4. **实现验证逻辑**:重写 `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]`
* **请求预校验方法**: 在请求数据完全构建后、发送前调用,用于静态检查。返回 `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`
```python
# 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`,您可以更好地控制复杂场景下测试用例的执行流程。