fix(exchange,case_validator),refactor(),feat(model): 解决 Pydantic 模型初始化与变量占位符的类型冲突,优化变量替换逻辑,重构 CaseInfo 模型并引入延迟校验机制
- 引入 SmartInt 和 SmartDict 类型,支持 YAML 占位符与业务类型的自动转换。 - 优化 CaseInfo 互斥校验逻辑,确保 request 与 api_action 二选一。 - 统一使用 Pydantic V2 的 model_config 规范。 - 将变量替换时机提前至模型实例化之前,支持占位符在校验前完成真实值注入, 保证了 int/bool 等字段的类型转换正确性。 - 优化断言渲染时机,支持响应提取值关联。
This commit is contained in:
@@ -15,7 +15,7 @@ from dataclasses import dataclass, asdict, field
|
||||
|
||||
import yaml
|
||||
|
||||
from commons.models import CaseInfo
|
||||
from commons.models import TestCaseStruct
|
||||
|
||||
|
||||
class CaseParser:
|
||||
@@ -23,15 +23,15 @@ class CaseParser:
|
||||
def to_yaml(case_data: dict) -> str:
|
||||
try:
|
||||
|
||||
CaseInfo(**case_data)
|
||||
TestCaseStruct(**case_data)
|
||||
except TypeError as error:
|
||||
logging.error(error)
|
||||
raise error
|
||||
return yaml.safe_dump(case_data, allow_unicode=True, sort_keys=False)
|
||||
|
||||
@staticmethod
|
||||
def from_yaml(yaml_str: str) -> CaseInfo:
|
||||
return CaseInfo(**yaml.safe_load(yaml_str))
|
||||
def from_yaml(yaml_str: str) -> TestCaseStruct:
|
||||
return TestCaseStruct(**yaml.safe_load(yaml_str))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -10,10 +10,18 @@
|
||||
@desc:
|
||||
"""
|
||||
import logging
|
||||
from typing import List, Union, Any
|
||||
|
||||
from pydantic import TypeAdapter
|
||||
|
||||
from core.exchange import Exchange
|
||||
from core.models import ValidateItem
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
VALIDATE_LIST_ADAPTER = TypeAdapter(List[ValidateItem])
|
||||
|
||||
class CaseValidator:
|
||||
VALIDATORS = {}
|
||||
|
||||
@@ -26,22 +34,46 @@ class CaseValidator:
|
||||
return decorator
|
||||
|
||||
@classmethod
|
||||
def assert_all(cls, validate: dict):
|
||||
if not validate:
|
||||
def validate(cls,response: Any, validate_list: List[ValidateItem]):
|
||||
"""
|
||||
核心断言入口:适配 CaseInfo.validate_data (List[ValidateItem])
|
||||
"""
|
||||
if not validate_list:
|
||||
return
|
||||
for assert_type, cases in validate.items():
|
||||
logger.info(f"键:{assert_type},值:{cases}")
|
||||
validator = cls.VALIDATORS.get(assert_type)
|
||||
logger.info(f"获取到的断言:{validator}")
|
||||
# dicts = [
|
||||
# item.model_dump(by_alias=True) if isinstance(item, ValidateItem) else item for item in validate_list
|
||||
# ]
|
||||
# rendered = exchanger.replace(dicts)
|
||||
# # 触发 SmartInt/SmartDict 类型修复
|
||||
# final_list = VALIDATE_LIST_ADAPTER.validate_python(rendered)
|
||||
|
||||
for item in validate_list:
|
||||
# 1. 提取模型中的数据
|
||||
# 此时 final_case 里的 item 已经是经过变量替换后的实体
|
||||
actual = item.check
|
||||
expect = item.expect
|
||||
method = item.assert_method # 即模型中的 alias="assert"
|
||||
msg = item.msg or f"Assert {actual} {method} {expect}"
|
||||
|
||||
# 2. 获取对应的断言函数
|
||||
validator = cls.VALIDATORS.get(method)
|
||||
if not validator:
|
||||
raise KeyError(f"Unsupported validator: {assert_type}")
|
||||
for msg, (a, b) in cases.items():
|
||||
validator(a, b, msg)
|
||||
logger.error(f"❌ 不支持的断言方式: {method}")
|
||||
raise KeyError(f"Unsupported validator: {method}")
|
||||
|
||||
# 3. 执行断言
|
||||
try:
|
||||
validator(actual, expect, msg)
|
||||
except AssertionError as e:
|
||||
logger.error(
|
||||
f"❌ 断言失败: {msg} | 实际值: {actual} ({type(actual)}), 期望值: {expect} ({type(expect)})")
|
||||
raise e
|
||||
|
||||
|
||||
@CaseValidator.register('equals')
|
||||
def validate_equals(a, b, msg):
|
||||
logger.info(f"assert {a} == {b}, {msg}执行这段代码")
|
||||
logger.info(f"assert {a} == {b}, {msg} 执行这段代码")
|
||||
print(f"assert {a} == {b}, {msg} 执行这段代码")
|
||||
assert a == b, msg
|
||||
|
||||
|
||||
@@ -64,17 +96,12 @@ def validate_not_contains(a, b, msg):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
mock_case = {
|
||||
"validate": {
|
||||
"equals": {
|
||||
"判断相等": ["Success.", "Success."]
|
||||
},
|
||||
"not_equals": {
|
||||
"判断不相等": ["Success.", "Suc."]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resp=None
|
||||
mock_case = [
|
||||
{"check": 100, "expect": 100, "assert": "equals"},
|
||||
{"check": "success", "expect": "success", "assert": "contains"}
|
||||
]
|
||||
final_validate_list = VALIDATE_LIST_ADAPTER.validate_python(mock_case)
|
||||
case_validator = CaseValidator()
|
||||
print(case_validator.VALIDATORS)
|
||||
case_validator.assert_all(mock_case.get("validate"))
|
||||
case_validator.validate(resp,final_validate_list)
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
"""
|
||||
from pathlib import Path
|
||||
|
||||
from commons.templates import Template
|
||||
from commons.file_processors.file_handle import FileHandle
|
||||
from core.templates import Template
|
||||
from commons.file_processors.yaml_processor import YamlProcessor as FileHandle
|
||||
|
||||
|
||||
class DataDriver:
|
||||
@@ -36,7 +36,7 @@ class DataDriver:
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
file_path = Path(r"E:\PyP\InterfaceAutoTest\TestCases\answer\test_1_status.yaml")
|
||||
file_path = Path(r"D:\CNWei\CNW\InterfaceAutoTest\test_cases\answer\test_1_status.yaml")
|
||||
|
||||
file_obj = FileHandle(file_path)
|
||||
print(file_path.stem)
|
||||
|
||||
Reference in New Issue
Block a user