添加error 测试用例,但是测试用例太复杂,还需要优化框架
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -26,45 +26,58 @@ class TestCaseRegistry:
|
||||
|
||||
def discover_test_cases(self):
|
||||
"""
|
||||
扫描指定目录,动态导入模块,并注册所有继承自 BaseAPITestCase 的类。
|
||||
扫描指定目录及其所有子目录,动态导入模块,并注册所有继承自 BaseAPITestCase 的类。
|
||||
"""
|
||||
if not os.path.isdir(self.test_cases_dir):
|
||||
self.logger.warning(f"测试用例目录不存在或不是一个目录: {self.test_cases_dir}")
|
||||
return
|
||||
|
||||
self.logger.info(f"开始从目录 '{self.test_cases_dir}' 发现测试用例...")
|
||||
self.logger.info(f"开始从目录 '{self.test_cases_dir}' 及其子目录发现测试用例...")
|
||||
found_count = 0
|
||||
for filename in os.listdir(self.test_cases_dir):
|
||||
if filename.endswith(".py") and not filename.startswith("__"):
|
||||
module_name = filename[:-3]
|
||||
file_path = os.path.join(self.test_cases_dir, filename)
|
||||
try:
|
||||
# 动态导入模块
|
||||
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
||||
if spec and spec.loader:
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
self.logger.debug(f"成功导入模块: {module_name} 从 {file_path}")
|
||||
# 使用 os.walk 进行递归扫描
|
||||
for root_dir, _, files in os.walk(self.test_cases_dir):
|
||||
for filename in files:
|
||||
if filename.endswith(".py") and not filename.startswith("__"):
|
||||
module_name = filename[:-3]
|
||||
file_path = os.path.join(root_dir, filename)
|
||||
try:
|
||||
# 动态导入模块
|
||||
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
||||
if spec and spec.loader:
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
self.logger.debug(f"成功导入模块: {module_name} 从 {file_path}")
|
||||
|
||||
# 在模块中查找 BaseAPITestCase 的子类
|
||||
for name, obj in inspect.getmembers(module):
|
||||
if inspect.isclass(obj) and issubclass(obj, BaseAPITestCase) and obj is not BaseAPITestCase:
|
||||
if obj.id in self._registry:
|
||||
self.logger.warning(f"发现重复的测试用例 ID: '{obj.id}' (来自类 '{obj.__name__}' in {file_path})。之前的定义将被覆盖。")
|
||||
|
||||
self._registry[obj.id] = obj
|
||||
if obj not in self._test_case_classes: # 避免重复添加同一个类对象
|
||||
# 在模块中查找 BaseAPITestCase 的子类
|
||||
for name, obj in inspect.getmembers(module):
|
||||
if inspect.isclass(obj) and issubclass(obj, BaseAPITestCase) and obj is not BaseAPITestCase:
|
||||
if not hasattr(obj, 'id') or not obj.id:
|
||||
self.logger.error(f"测试用例类 '{obj.__name__}' 在文件 '{file_path}' 中缺少有效的 'id' 属性,已跳过注册。")
|
||||
continue
|
||||
|
||||
if obj.id in self._registry:
|
||||
self.logger.warning(f"发现重复的测试用例 ID: '{obj.id}' (来自类 '{obj.__name__}' in {file_path})。之前的定义将被覆盖。")
|
||||
|
||||
self._registry[obj.id] = obj
|
||||
# 更新 _test_case_classes 列表:如果已存在相同ID的类,替换它;否则添加。
|
||||
# 这确保了排序时使用的是最新的类定义,以防ID冲突。
|
||||
existing_class_indices = [i for i, tc_class in enumerate(self._test_case_classes) if tc_class.id == obj.id]
|
||||
if existing_class_indices:
|
||||
for index in sorted(existing_class_indices, reverse=True): # 从后往前删除,避免索引问题
|
||||
self.logger.debug(f"从 _test_case_classes 列表中移除旧的同ID ('{obj.id}') 测试用例类: {self._test_case_classes[index].__name__}")
|
||||
del self._test_case_classes[index]
|
||||
|
||||
self._test_case_classes.append(obj)
|
||||
found_count += 1
|
||||
self.logger.info(f"已注册测试用例: '{obj.id}' ({obj.name}) 来自类 '{obj.__name__}'")
|
||||
else:
|
||||
self.logger.error(f"无法为文件 '{file_path}' 创建模块规范。")
|
||||
except ImportError as e:
|
||||
self.logger.error(f"导入模块 '{module_name}' 从 '{file_path}' 失败: {e}", exc_info=True)
|
||||
except AttributeError as e:
|
||||
self.logger.error(f"在模块 '{module_name}' ({file_path}) 中查找测试用例时出错 (可能是缺少必要的元数据如 'id'): {e}", exc_info=True)
|
||||
except Exception as e:
|
||||
self.logger.error(f"处理文件 '{file_path}' 时发生未知错误: {e}", exc_info=True)
|
||||
found_count += 1
|
||||
self.logger.info(f"已注册测试用例: '{obj.id}' ({getattr(obj, 'name', 'N/A')}) 来自类 '{obj.__name__}' (路径: {file_path})")
|
||||
else:
|
||||
self.logger.error(f"无法为文件 '{file_path}' 创建模块规范。")
|
||||
except ImportError as e:
|
||||
self.logger.error(f"导入模块 '{module_name}' 从 '{file_path}' 失败: {e}", exc_info=True)
|
||||
except AttributeError as e:
|
||||
self.logger.error(f"在模块 '{module_name}' ({file_path}) 中查找测试用例时出错: {e}", exc_info=True)
|
||||
except Exception as e:
|
||||
self.logger.error(f"处理文件 '{file_path}' 时发生未知错误: {e}", exc_info=True)
|
||||
|
||||
# 根据 execution_order 对收集到的测试用例类进行排序
|
||||
try:
|
||||
|
||||
@@ -13,6 +13,7 @@ from enum import Enum
|
||||
import datetime
|
||||
import datetime as dt
|
||||
from uuid import UUID
|
||||
from dataclasses import asdict as dataclass_asdict, is_dataclass # New import
|
||||
|
||||
from pydantic import BaseModel, Field, create_model
|
||||
from pydantic.networks import EmailStr
|
||||
@@ -621,54 +622,182 @@ class APITestOrchestrator:
|
||||
global_api_spec: Union[ParsedYAPISpec, ParsedSwaggerSpec] # 整个API的规格
|
||||
) -> ExecutedTestCaseResult:
|
||||
"""
|
||||
实例化并执行单个APITestCase。
|
||||
"""
|
||||
tc_start_time = time.time()
|
||||
validation_points: List[ValidationResult] = []
|
||||
test_case_instance: Optional[BaseAPITestCase] = None
|
||||
|
||||
endpoint_spec_dict: Dict[str, Any]
|
||||
# 确保 endpoint_spec 转换为字典,以便在测试用例和请求上下文中统一使用
|
||||
if hasattr(endpoint_spec, 'to_dict') and callable(endpoint_spec.to_dict):
|
||||
endpoint_spec_dict = endpoint_spec.to_dict()
|
||||
elif isinstance(endpoint_spec, dict): # 如果它已经是字典 (例如从 OpenAPI 解析器直接过来)
|
||||
endpoint_spec_dict = endpoint_spec
|
||||
elif isinstance(endpoint_spec, (YAPIEndpoint, SwaggerEndpoint)): # 作为后备,从特定类型提取
|
||||
self.logger.debug(f"Manually converting endpoint_spec of type {type(endpoint_spec).__name__} to dict.")
|
||||
endpoint_spec_dict = {
|
||||
"method": getattr(endpoint_spec, 'method', 'UNKNOWN_METHOD'),
|
||||
"path": getattr(endpoint_spec, 'path', 'UNKNOWN_PATH'),
|
||||
"title": getattr(endpoint_spec, 'title', getattr(endpoint_spec, 'summary', '')),
|
||||
"summary": getattr(endpoint_spec, 'summary', ''),
|
||||
"description": getattr(endpoint_spec, 'description', ''),
|
||||
"operationId": getattr(endpoint_spec, 'operation_id',
|
||||
f"{getattr(endpoint_spec, 'method', '').upper()}_{getattr(endpoint_spec, 'path', '').replace('/', '_')}"),
|
||||
# 尝试提取参数和请求体 (简化版)
|
||||
"parameters": getattr(endpoint_spec, 'parameters', []) if isinstance(endpoint_spec, SwaggerEndpoint) else (getattr(endpoint_spec, 'req_query', []) + getattr(endpoint_spec, 'req_headers', [])),
|
||||
"requestBody": getattr(endpoint_spec, 'request_body', None) if isinstance(endpoint_spec, SwaggerEndpoint) else getattr(endpoint_spec, 'req_body_other', None),
|
||||
"_original_object_type": type(endpoint_spec).__name__
|
||||
}
|
||||
else:
|
||||
endpoint_spec_dict = {}
|
||||
self.logger.warning(f"endpoint_spec无法转换为字典,实际类型: {type(endpoint_spec)}")
|
||||
执行单个测试用例。
|
||||
|
||||
global_api_spec_dict: Dict[str, Any]
|
||||
if hasattr(global_api_spec, 'to_dict') and callable(global_api_spec.to_dict):
|
||||
global_api_spec_dict = global_api_spec.to_dict()
|
||||
elif isinstance(global_api_spec, dict):
|
||||
global_api_spec_dict = global_api_spec
|
||||
else:
|
||||
global_api_spec_dict = {}
|
||||
self.logger.warning(f"global_api_spec无法转换为字典,实际类型: {type(global_api_spec)}")
|
||||
流程:
|
||||
1. 准备请求数据 (路径参数, 查询参数, 请求头, 请求体)。
|
||||
- 首先尝试从测试用例的 generate_xxx 方法获取。
|
||||
- 如果测试用例未覆盖或返回None,则尝试从API spec生成默认数据。
|
||||
- 如果开启了LLM,并且测试用例允许,则使用LLM生成。
|
||||
2. (如果适用) 调用测试用例的 modify_request_url 钩子。
|
||||
3. (如果适用) 调用测试用例的 validate_request_url, validate_request_headers, validate_request_body 钩子。
|
||||
4. 发送API请求。
|
||||
5. 记录响应。
|
||||
6. 调用测试用例的 validate_response 和 check_performance 钩子。
|
||||
7. 汇总验证结果,确定测试用例状态。
|
||||
"""
|
||||
start_time = time.monotonic()
|
||||
validation_results: List[ValidationResult] = []
|
||||
overall_status: ExecutedTestCaseResult.Status
|
||||
execution_message = ""
|
||||
|
||||
# 将 endpoint_spec 转换为字典,如果它还不是的话
|
||||
endpoint_spec_dict: Dict[str, Any]
|
||||
if isinstance(endpoint_spec, dict):
|
||||
endpoint_spec_dict = endpoint_spec
|
||||
self.logger.debug(f"endpoint_spec 已经是字典类型。")
|
||||
elif hasattr(endpoint_spec, 'to_dict') and callable(endpoint_spec.to_dict):
|
||||
try:
|
||||
endpoint_spec_dict = endpoint_spec.to_dict()
|
||||
self.logger.debug(f"成功通过 to_dict() 方法将类型为 {type(endpoint_spec)} 的 endpoint_spec 转换为字典。")
|
||||
if not endpoint_spec_dict: # 如果 to_dict() 返回空字典
|
||||
self.logger.warning(f"endpoint_spec.to_dict() (类型: {type(endpoint_spec)}) 返回了一个空字典。")
|
||||
# 尝试备用转换
|
||||
if isinstance(endpoint_spec, (YAPIEndpoint, SwaggerEndpoint)):
|
||||
self.logger.debug(f"尝试从 {type(endpoint_spec).__name__} 对象的属性手动构建 endpoint_spec_dict。")
|
||||
endpoint_spec_dict = {
|
||||
"method": getattr(endpoint_spec, 'method', 'UNKNOWN_METHOD').upper(),
|
||||
"path": getattr(endpoint_spec, 'path', 'UNKNOWN_PATH'),
|
||||
"title": getattr(endpoint_spec, 'title', getattr(endpoint_spec, 'summary', '')),
|
||||
"summary": getattr(endpoint_spec, 'summary', ''),
|
||||
"description": getattr(endpoint_spec, 'description', ''),
|
||||
"operationId": getattr(endpoint_spec, 'operation_id', f"{getattr(endpoint_spec, 'method', '').upper()}_{getattr(endpoint_spec, 'path', '').replace('/', '_')}"),
|
||||
"parameters": getattr(endpoint_spec, 'parameters', []) if hasattr(endpoint_spec, 'parameters') else (getattr(endpoint_spec, 'req_query', []) + getattr(endpoint_spec, 'req_headers', [])),
|
||||
"requestBody": getattr(endpoint_spec, 'request_body', None) if hasattr(endpoint_spec, 'request_body') else getattr(endpoint_spec, 'req_body_other', None),
|
||||
"_original_object_type": type(endpoint_spec).__name__
|
||||
}
|
||||
if not any(endpoint_spec_dict.values()): # 如果手动构建后仍基本为空
|
||||
self.logger.error(f"手动从属性构建 endpoint_spec_dict (类型: {type(endpoint_spec)}) 后仍然为空或无效。")
|
||||
endpoint_spec_dict = {} # 重置为空,触发下方错误处理
|
||||
except Exception as e:
|
||||
self.logger.error(f"调用 endpoint_spec (类型: {type(endpoint_spec)}) 的 to_dict() 方法时出错: {e}。尝试备用转换。")
|
||||
if isinstance(endpoint_spec, (YAPIEndpoint, SwaggerEndpoint)):
|
||||
self.logger.debug(f"尝试从 {type(endpoint_spec).__name__} 对象的属性手动构建 endpoint_spec_dict。")
|
||||
endpoint_spec_dict = {
|
||||
"method": getattr(endpoint_spec, 'method', 'UNKNOWN_METHOD').upper(),
|
||||
"path": getattr(endpoint_spec, 'path', 'UNKNOWN_PATH'),
|
||||
"title": getattr(endpoint_spec, 'title', getattr(endpoint_spec, 'summary', '')),
|
||||
"summary": getattr(endpoint_spec, 'summary', ''),
|
||||
"description": getattr(endpoint_spec, 'description', ''),
|
||||
"operationId": getattr(endpoint_spec, 'operation_id', f"{getattr(endpoint_spec, 'method', '').upper()}_{getattr(endpoint_spec, 'path', '').replace('/', '_')}"),
|
||||
"parameters": getattr(endpoint_spec, 'parameters', []) if hasattr(endpoint_spec, 'parameters') else (getattr(endpoint_spec, 'req_query', []) + getattr(endpoint_spec, 'req_headers', [])),
|
||||
"requestBody": getattr(endpoint_spec, 'request_body', None) if hasattr(endpoint_spec, 'request_body') else getattr(endpoint_spec, 'req_body_other', None),
|
||||
"_original_object_type": type(endpoint_spec).__name__
|
||||
}
|
||||
if not any(endpoint_spec_dict.values()): # 如果手动构建后仍基本为空
|
||||
self.logger.error(f"手动从属性构建 endpoint_spec_dict (类型: {type(endpoint_spec)}) 后仍然为空或无效。")
|
||||
endpoint_spec_dict = {} # 重置为空,触发下方错误处理
|
||||
else:
|
||||
endpoint_spec_dict = {} # 转换失败
|
||||
elif hasattr(endpoint_spec, 'data') and isinstance(getattr(endpoint_spec, 'data'), dict): # 兼容 YAPIEndpoint 结构
|
||||
endpoint_spec_dict = getattr(endpoint_spec, 'data')
|
||||
self.logger.debug(f"使用了类型为 {type(endpoint_spec)} 的 endpoint_spec 的 .data 属性。")
|
||||
else: # 如果没有 to_dict, 也不是已知可直接访问 .data 的类型,则尝试最后的通用转换或手动构建
|
||||
if isinstance(endpoint_spec, (YAPIEndpoint, SwaggerEndpoint)):
|
||||
self.logger.debug(f"类型为 {type(endpoint_spec).__name__} 的 endpoint_spec 没有 to_dict() 或 data,尝试从属性手动构建。")
|
||||
endpoint_spec_dict = {
|
||||
"method": getattr(endpoint_spec, 'method', 'UNKNOWN_METHOD').upper(),
|
||||
"path": getattr(endpoint_spec, 'path', 'UNKNOWN_PATH'),
|
||||
"title": getattr(endpoint_spec, 'title', getattr(endpoint_spec, 'summary', '')),
|
||||
"summary": getattr(endpoint_spec, 'summary', ''),
|
||||
"description": getattr(endpoint_spec, 'description', ''),
|
||||
"operationId": getattr(endpoint_spec, 'operation_id', f"{getattr(endpoint_spec, 'method', '').upper()}_{getattr(endpoint_spec, 'path', '').replace('/', '_')}"),
|
||||
"parameters": getattr(endpoint_spec, 'parameters', []) if hasattr(endpoint_spec, 'parameters') else (getattr(endpoint_spec, 'req_query', []) + getattr(endpoint_spec, 'req_headers', [])),
|
||||
"requestBody": getattr(endpoint_spec, 'request_body', None) if hasattr(endpoint_spec, 'request_body') else getattr(endpoint_spec, 'req_body_other', None),
|
||||
"_original_object_type": type(endpoint_spec).__name__
|
||||
}
|
||||
if not any(endpoint_spec_dict.values()): # 如果手动构建后仍基本为空
|
||||
self.logger.error(f"手动从属性构建 endpoint_spec_dict (类型: {type(endpoint_spec)}) 后仍然为空或无效。")
|
||||
endpoint_spec_dict = {} # 重置为空,触发下方错误处理
|
||||
else:
|
||||
try:
|
||||
endpoint_spec_dict = dict(endpoint_spec)
|
||||
self.logger.warning(f"直接将类型为 {type(endpoint_spec)} 的 endpoint_spec 转换为字典。这可能是一个浅拷贝,并且可能不完整。")
|
||||
except TypeError:
|
||||
self.logger.error(f"无法将 endpoint_spec (类型: {type(endpoint_spec)}) 转换为字典,也未找到有效的转换方法。")
|
||||
endpoint_spec_dict = {}
|
||||
|
||||
if not endpoint_spec_dict or not endpoint_spec_dict.get("path") or endpoint_spec_dict.get("path") == 'UNKNOWN_PATH': # 如果转换后仍为空或无效
|
||||
self.logger.error(f"Endpoint spec (原始类型: {type(endpoint_spec)}) 无法有效转换为包含有效路径的字典,测试用例执行可能受影响。最终 endpoint_spec_dict: {endpoint_spec_dict}")
|
||||
# 创建一个最小的 endpoint_spec_dict 以允许测试用例实例化,但它将缺少大部分信息
|
||||
endpoint_spec_dict = {
|
||||
'method': endpoint_spec_dict.get('method', 'UNKNOWN_METHOD'), # 保留已解析的方法
|
||||
'path': 'UNKNOWN_PATH_CONVERSION_FAILED',
|
||||
'title': f"Unknown endpoint due to spec conversion error for original type {type(endpoint_spec)}",
|
||||
'parameters': [], # 确保有空的 parameters 和 requestBody
|
||||
'requestBody': None
|
||||
}
|
||||
|
||||
# 确保 global_api_spec (应该是 ParsedSwaggerSpec 或 ParsedYAPISpec 实例) 被转换为字典
|
||||
global_spec_dict: Dict[str, Any] = {}
|
||||
converted_by_method: Optional[str] = None
|
||||
|
||||
if hasattr(global_api_spec, 'spec') and isinstance(getattr(global_api_spec, 'spec', None), dict) and getattr(global_api_spec, 'spec', None):
|
||||
global_spec_dict = global_api_spec.spec # type: ignore
|
||||
converted_by_method = ".spec attribute"
|
||||
elif is_dataclass(global_api_spec) and not isinstance(global_api_spec, type): # Ensure it's an instance, not the class itself
|
||||
try:
|
||||
candidate_spec = dataclass_asdict(global_api_spec)
|
||||
if isinstance(candidate_spec, dict) and candidate_spec:
|
||||
global_spec_dict = candidate_spec
|
||||
converted_by_method = "dataclasses.asdict()"
|
||||
except Exception as e:
|
||||
self.logger.debug(f"Calling dataclasses.asdict() on {type(global_api_spec)} failed: {e}, trying other methods.")
|
||||
|
||||
if not global_spec_dict and hasattr(global_api_spec, 'model_dump') and callable(global_api_spec.model_dump):
|
||||
try:
|
||||
candidate_spec = global_api_spec.model_dump()
|
||||
if isinstance(candidate_spec, dict) and candidate_spec:
|
||||
global_spec_dict = candidate_spec
|
||||
converted_by_method = ".model_dump()"
|
||||
except Exception as e:
|
||||
self.logger.debug(f"Calling .model_dump() on {type(global_api_spec)} failed: {e}, trying other methods.")
|
||||
|
||||
if not global_spec_dict and hasattr(global_api_spec, 'dict') and callable(global_api_spec.dict):
|
||||
try:
|
||||
candidate_spec = global_api_spec.dict()
|
||||
if isinstance(candidate_spec, dict) and candidate_spec:
|
||||
global_spec_dict = candidate_spec
|
||||
converted_by_method = ".dict()"
|
||||
except Exception as e:
|
||||
self.logger.debug(f"Calling .dict() on {type(global_api_spec)} failed: {e}, trying other methods.")
|
||||
|
||||
if not global_spec_dict and hasattr(global_api_spec, 'to_dict') and callable(global_api_spec.to_dict):
|
||||
try:
|
||||
candidate_spec = global_api_spec.to_dict()
|
||||
if isinstance(candidate_spec, dict) and candidate_spec:
|
||||
global_spec_dict = candidate_spec
|
||||
converted_by_method = ".to_dict()"
|
||||
except Exception as e:
|
||||
self.logger.debug(f"Calling .to_dict() on {type(global_api_spec)} failed: {e}, trying other methods.")
|
||||
|
||||
if not global_spec_dict and isinstance(global_api_spec, dict) and global_api_spec:
|
||||
global_spec_dict = global_api_spec
|
||||
converted_by_method = "direct dict"
|
||||
self.logger.warning(f"global_api_spec was already a dictionary. This might be unexpected if an object was anticipated.")
|
||||
|
||||
if global_spec_dict and converted_by_method:
|
||||
self.logger.debug(f"Successfully converted/retrieved global_api_spec (type: {type(global_api_spec)}) to dict using {converted_by_method}.")
|
||||
elif not global_spec_dict :
|
||||
self.logger.error(
|
||||
f"Failed to convert global_api_spec (type: {type(global_api_spec)}) to a non-empty dictionary using .spec, dataclasses.asdict(), .model_dump(), .dict(), or .to_dict(). "
|
||||
f"It's also not a non-empty dictionary itself. JSON reference resolution will be severely limited or fail. Using empty global_spec_dict."
|
||||
)
|
||||
global_spec_dict = {}
|
||||
|
||||
# 将 global_spec_dict 注入到 endpoint_spec_dict 中,供可能的内部解析使用 (如果 to_dict 未包含它)
|
||||
if '_global_api_spec_for_resolution' not in endpoint_spec_dict and global_spec_dict:
|
||||
endpoint_spec_dict['_global_api_spec_for_resolution'] = global_spec_dict
|
||||
|
||||
|
||||
try:
|
||||
self.logger.debug(f"准备实例化测试用例类: {test_case_class.__name__} 使用 endpoint_spec (keys: {list(endpoint_spec_dict.keys()) if endpoint_spec_dict else 'None'}) 和 global_api_spec (keys: {list(global_spec_dict.keys()) if global_spec_dict else 'None'})")
|
||||
test_case_instance = test_case_class(
|
||||
endpoint_spec=endpoint_spec_dict,
|
||||
global_api_spec=global_api_spec_dict,
|
||||
json_schema_validator=self.validator # <--- 注入 JSONSchemaValidator
|
||||
endpoint_spec=endpoint_spec_dict,
|
||||
global_api_spec=global_spec_dict,
|
||||
json_schema_validator=self.validator
|
||||
)
|
||||
test_case_instance.logger.info(f"开始执行测试用例 '{test_case_instance.id}' for endpoint '{endpoint_spec_dict.get('method')} {endpoint_spec_dict.get('path')}'")
|
||||
self.logger.info(f"开始执行测试用例 '{test_case_instance.id}' ({test_case_instance.name}) for endpoint '{endpoint_spec_dict.get('method', 'N/A')} {endpoint_spec_dict.get('path', 'N/A')}'")
|
||||
|
||||
# 调用 _prepare_initial_request_data 时传递 test_case_instance
|
||||
# 并直接解包返回的元组
|
||||
@@ -722,26 +851,26 @@ class APITestOrchestrator:
|
||||
endpoint_spec=endpoint_spec_dict
|
||||
)
|
||||
|
||||
validation_points.extend(test_case_instance.validate_request_url(api_request_context.url, api_request_context))
|
||||
validation_points.extend(test_case_instance.validate_request_headers(api_request_context.headers, api_request_context))
|
||||
validation_points.extend(test_case_instance.validate_request_body(api_request_context.body, api_request_context))
|
||||
validation_results.extend(test_case_instance.validate_request_url(api_request_context.url, api_request_context))
|
||||
validation_results.extend(test_case_instance.validate_request_headers(api_request_context.headers, api_request_context))
|
||||
validation_results.extend(test_case_instance.validate_request_body(api_request_context.body, api_request_context))
|
||||
|
||||
critical_pre_validation_failure = False
|
||||
failure_messages = []
|
||||
for vp in validation_points:
|
||||
for vp in validation_results:
|
||||
if not vp.passed and test_case_instance.severity in [TestSeverity.CRITICAL, TestSeverity.HIGH]: # Check severity of the Test Case for pre-validation
|
||||
critical_pre_validation_failure = True
|
||||
failure_messages.append(vp.message)
|
||||
|
||||
if critical_pre_validation_failure:
|
||||
self.logger.warning(f"测试用例 '{test_case_instance.id}' 因请求预校验失败而中止 (TC严重级别: {test_case_instance.severity.value})。失败信息: {'; '.join(failure_messages)}")
|
||||
tc_duration = time.time() - tc_start_time
|
||||
tc_duration = time.monotonic() - start_time
|
||||
return ExecutedTestCaseResult(
|
||||
test_case_id=test_case_instance.id,
|
||||
test_case_name=test_case_instance.name,
|
||||
test_case_severity=test_case_instance.severity,
|
||||
status=ExecutedTestCaseResult.Status.FAILED,
|
||||
validation_points=validation_points,
|
||||
validation_points=validation_results,
|
||||
message=f"请求预校验失败: {'; '.join(failure_messages)}",
|
||||
duration=tc_duration
|
||||
)
|
||||
@@ -781,32 +910,32 @@ class APITestOrchestrator:
|
||||
request_context=api_request_context
|
||||
)
|
||||
|
||||
validation_points.extend(test_case_instance.validate_response(api_response_context, api_request_context))
|
||||
validation_points.extend(test_case_instance.check_performance(api_response_context, api_request_context))
|
||||
validation_results.extend(test_case_instance.validate_response(api_response_context, api_request_context))
|
||||
validation_results.extend(test_case_instance.check_performance(api_response_context, api_request_context))
|
||||
|
||||
final_status = ExecutedTestCaseResult.Status.PASSED
|
||||
if any(not vp.passed for vp in validation_points):
|
||||
if any(not vp.passed for vp in validation_results):
|
||||
final_status = ExecutedTestCaseResult.Status.FAILED
|
||||
|
||||
tc_duration = time.time() - tc_start_time
|
||||
tc_duration = time.monotonic() - start_time
|
||||
return ExecutedTestCaseResult(
|
||||
test_case_id=test_case_instance.id,
|
||||
test_case_name=test_case_instance.name,
|
||||
test_case_severity=test_case_instance.severity,
|
||||
status=final_status,
|
||||
validation_points=validation_points,
|
||||
validation_points=validation_results,
|
||||
duration=tc_duration
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"执行测试用例 '{test_case_class.id if test_case_instance else test_case_class.__name__}' 时发生严重错误: {e}", exc_info=True)
|
||||
tc_duration = time.time() - tc_start_time
|
||||
tc_duration = time.monotonic() - start_time
|
||||
return ExecutedTestCaseResult(
|
||||
test_case_id=test_case_instance.id if test_case_instance else test_case_class.id if hasattr(test_case_class, 'id') else "unknown_tc_id",
|
||||
test_case_name=test_case_instance.name if test_case_instance else test_case_class.name if hasattr(test_case_class, 'name') else "Unknown Test Case Name",
|
||||
test_case_severity=test_case_instance.severity if test_case_instance else TestSeverity.CRITICAL,
|
||||
status=ExecutedTestCaseResult.Status.ERROR,
|
||||
validation_points=validation_points,
|
||||
validation_points=validation_results,
|
||||
message=f"测试用例执行时发生内部错误: {str(e)}",
|
||||
duration=tc_duration
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user