- 优化 WorkflowExecutor 与 Exchange支持 ExecutionEnv 资源注入。 - 实现 Session 级别连接复用与变量池内存镜像化,消除重复 I/O 开销。 - 引入 ChainMap 实现动态上下文切换,解决参数化变量与全局提取变量的优先级覆盖。 - 完善变量提取与断言逻辑,确保跨用例变量流转的可靠性。
129 lines
4.5 KiB
Python
129 lines
4.5 KiB
Python
#!/usr/bin/env python
|
||
# coding=utf-8
|
||
|
||
"""
|
||
@author: chen wei
|
||
@Software: PyCharm
|
||
@contact: t6i888@163.com
|
||
@file: models.py
|
||
@date: 2024 2024/9/15 21:14
|
||
@desc: 声明yaml用例格式
|
||
"""
|
||
import logging
|
||
from typing import List, Any
|
||
|
||
from pydantic import BaseModel, Field, ConfigDict
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class HttpAction(BaseModel):
|
||
method: str = Field(..., description="HTTP 请求方法: get, post, etc.")
|
||
url: str = Field(..., description="接口路径或完整 URL")
|
||
headers: dict[str, Any] | None = Field(default=None, description="HTTP 请求头")
|
||
params: dict[str, Any] | None = Field(default=None, description="URL 查询参数")
|
||
data: dict[str, Any] | None = None
|
||
json_body: Any | None = Field(default=None, alias="json")
|
||
timeout: int = 10
|
||
files: dict[str, Any] | None = None
|
||
|
||
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
||
|
||
|
||
class ApiActionModel(BaseModel):
|
||
module: str = Field(..., alias="class", description="要调用的 API 类名")
|
||
method: str = Field(..., description="类中的方法名")
|
||
params: dict[str, Any] = Field(default_factory=dict, description="传给方法的参数")
|
||
|
||
model_config = ConfigDict(populate_by_name=True)
|
||
|
||
|
||
class ValidateItem(BaseModel):
|
||
check: str = Field(..., description="要检查的字段或表达式")
|
||
assert_method: str = Field(alias="assert", default="equals")
|
||
expect: Any = Field(..., description="期望值")
|
||
msg: str = Field(default="Assertion", description="断言描述")
|
||
|
||
model_config = ConfigDict(populate_by_name=True)
|
||
|
||
|
||
class RawSchema(BaseModel):
|
||
title: str = Field(..., description="用例标题")
|
||
epic: str | None = None
|
||
feature: str | None = None
|
||
story: str | None = None
|
||
# 统一使用 action 字段承载业务逻辑 (Http 或 PO)
|
||
action: dict[str, Any] = Field(description="请求内容或PO动作内容")
|
||
extract: dict[str, List[Any]] | None = Field(
|
||
default=None,
|
||
description="变量提取表达式,格式: {变量名: [来源, 表达式, 索引]}"
|
||
)
|
||
validate_data: List[Any] = Field(
|
||
default_factory=list,
|
||
alias="validate",
|
||
description="断言信息"
|
||
)
|
||
|
||
model_config = ConfigDict(extra="allow",
|
||
populate_by_name=True, # 无论是在代码中用 api_class 还是在 YAML 中用 class 赋值,Pydantic 都能正确识别。
|
||
arbitrary_types_allowed=True # 允许在模型中使用非 Pydantic 标准类型(如自定义类实例)
|
||
) # 允许参数化等额外字段
|
||
|
||
def is_po_mode(self) -> bool:
|
||
"""判断是否为 PO 模式"""
|
||
return "class" in self.action or "module" in self.action
|
||
|
||
|
||
if __name__ == '__main__':
|
||
# 模拟数据 1:标准请求模式
|
||
raw_case_1 = {
|
||
"title": "查询状态信息",
|
||
"action": {
|
||
"method": "get",
|
||
"url": "/api/v1/info",
|
||
"headers": {"User-Agent": "pytest-ai"},
|
||
"json": {"User-Agent": "pytest-ai"}
|
||
},
|
||
"validate": [
|
||
{"check": "status_code", "assert": "equals", "expect": 200, "msg": "响应码200"},
|
||
{"check": "$.msg", "expect": "Success"}
|
||
]
|
||
}
|
||
|
||
# 模拟数据 2:PO 模式 (反射调用)
|
||
raw_case_2 = {
|
||
"title": "用户登录测试",
|
||
"action": {
|
||
"class": "UserAPI",
|
||
"method": "login",
|
||
"params": {"user": "admin", "pwd": "123"}
|
||
},
|
||
"extract": {
|
||
"token": ["json", "$.data.token", 0]
|
||
}
|
||
}
|
||
|
||
print("--- 开始模型校验测试 ---\n")
|
||
|
||
try:
|
||
# 验证模式 1
|
||
case1 = RawSchema(**raw_case_1)
|
||
print(f"✅ 模式1 (Request) 校验通过: {case1.title}")
|
||
print(f" http: {case1.action}")
|
||
print(f" 断言规则数: {len(case1.validate_data)}\n")
|
||
|
||
# 验证模式 2
|
||
case2 = RawSchema(**raw_case_2)
|
||
print(f"✅ 模式2 (PO Mode) 校验通过: {case2.title}")
|
||
print(f" api: {case2.action}")
|
||
print(f" 提取规则数: {len(case2.extract)}\n")
|
||
|
||
# 验证非法数据(如:既没有 request 也没有 api_action 的情况可以在业务层进一步校验)
|
||
# 这里演示 Pydantic 自动类型转换
|
||
invalid_data = {"title": "错误用例", "action": {"url": "/api"}} # 缺少 method
|
||
print("--- 预期失败测试 ---")
|
||
RawSchema(**invalid_data)
|
||
|
||
except Exception as e:
|
||
print(f"❌ 预期内的校验失败: \n{e}")
|