feat(core): 增强 Exchange,实现智能变量替换与类型保持
- 优化 conftest.py 增加异常日志记录和测试报告环境信息 - 其他优化
This commit is contained in:
131
conftest.py
131
conftest.py
@@ -4,57 +4,136 @@
|
||||
"""
|
||||
@desc: Pytest 配置文件,用于设置全局 Fixture 和钩子函数
|
||||
"""
|
||||
import platform
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
import logging
|
||||
|
||||
from core import settings
|
||||
from commons.files import YamlFile
|
||||
from core.context import VariableStore, ExecutionEnv
|
||||
from core.executor import WorkflowExecutor
|
||||
from core.models import RawSchema
|
||||
from core.session import Session
|
||||
from core.exchange import Exchange
|
||||
from core.settings import EXTRACT_CACHE
|
||||
from core.settings import EXTRACT_CACHE,base_url
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def api_env():
|
||||
# 注册命令行参数
|
||||
def pytest_addoption(parser: Any) -> None:
|
||||
"""
|
||||
工业级资源调度器
|
||||
1. 保持全局单 Session (连接池复用)
|
||||
2. 变量池 L2 内存镜像化 (减少 I/O)
|
||||
注册自定义命令行参数。
|
||||
|
||||
允许用户通过命令行传递参数来控制测试执行的行为。
|
||||
|
||||
Args:
|
||||
parser: Pytest 的命令行参数解析器对象。
|
||||
"""
|
||||
parser.addoption("--test_dir", action="store", default=None, help="测试用例目录")
|
||||
parser.addoption("--env", action="store", default="test", help="运行环境标识 (test/prod/dev)")
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def execution_context():
|
||||
"""
|
||||
[Session级别 Fixture] 全局执行上下文环境。
|
||||
|
||||
职责:
|
||||
1. 生命周期管理:初始化并管理全局唯一的 Session、变量存储 (Store) 和 变量交换器 (Exchanger)。
|
||||
2. 资源复用:确保 HTTP 连接池复用,减少握手开销。
|
||||
3. 数据持久化:在测试结束时自动将提取的变量回写到磁盘。
|
||||
|
||||
Yields:
|
||||
ExecutionEnv: 包含 session, store, exchanger 的环境对象实例。
|
||||
"""
|
||||
# Setup: 加载环境
|
||||
store = VariableStore(settings.DATA_DIR / "extract.yaml")
|
||||
store = VariableStore(EXTRACT_CACHE)
|
||||
exchanger = Exchange(variable_cache=store.store)
|
||||
session = Session(settings.base_url)
|
||||
executor = WorkflowExecutor()
|
||||
session = Session(base_url)
|
||||
|
||||
env = ExecutionEnv(session, store, executor, exchanger)
|
||||
env = ExecutionEnv(session, store, exchanger)
|
||||
|
||||
yield env # 注入到测试用例中
|
||||
|
||||
# Teardown: 统一持久化与清理
|
||||
store.persist()
|
||||
session.close()
|
||||
@pytest.fixture(scope="session")
|
||||
def session():
|
||||
"""全局共享的 Session Fixture"""
|
||||
return Session(settings.base_url)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def exchanger():
|
||||
"""全局共享的 Exchange Fixture"""
|
||||
return Exchange(EXTRACT_CACHE)
|
||||
def pytest_exception_interact(node: Any, call: Any, report: Any) -> None:
|
||||
"""
|
||||
[Hook] 异常交互钩子。
|
||||
|
||||
当测试用例执行失败(断言错误或代码异常)时触发。
|
||||
主要用于捕获详细的错误堆栈信息,并将其格式化输出到日志中,
|
||||
以便于在控制台或日志文件中快速定位问题。
|
||||
|
||||
# @pytest.fixture(scope="session")
|
||||
# def case_engine(session, exchanger):
|
||||
# """全局共享的 CaseEngine Fixture"""
|
||||
# return CaseEngine(session, exchanger)
|
||||
Args:
|
||||
node: 发生异常的测试节点(Item 或 Collector)。
|
||||
call: 测试调用信息(包含 excinfo 异常信息)。
|
||||
report: 测试报告对象。
|
||||
"""
|
||||
if report.failed:
|
||||
# 获取详细的错误堆栈(包含 assert 的对比信息)
|
||||
# long,short,no-locals
|
||||
exc_info = call.excinfo.getrepr(style='short')
|
||||
logger.error(f"\n{'=' * 40} TEST FAILED {'=' * 40}\n"
|
||||
f"Node: {node.name}\n"
|
||||
f"Error:\n{exc_info}"
|
||||
)
|
||||
logger.error("=" * 93 + "\n")
|
||||
|
||||
def pytest_sessionfinish(session: Any, exitstatus: int) -> None:
|
||||
"""
|
||||
[Hook] 会话结束钩子。
|
||||
|
||||
在所有测试执行完毕后调用。主要完成以下工作:
|
||||
1. 根据退出状态码记录不同级别的日志信息。
|
||||
2. 收集测试环境信息(如 Base URL, Python 版本, 操作系统等)。
|
||||
3. 生成 `environment.properties` 文件供 Allure 报告展示。
|
||||
|
||||
Args:
|
||||
session: Pytest 会话对象。
|
||||
exitstatus: 整体测试执行的退出状态码。
|
||||
"""
|
||||
match exitstatus:
|
||||
case pytest.ExitCode.OK:
|
||||
logging.info("测试全部通过!")
|
||||
case pytest.ExitCode.TESTS_FAILED:
|
||||
logging.warning("部分测试用例执行失败,请检查报告。")
|
||||
case pytest.ExitCode.INTERRUPTED:
|
||||
logging.error("测试被用户手动中断(Ctrl+C)。")
|
||||
case pytest.ExitCode.INTERNAL_ERROR:
|
||||
logging.critical("Pytest 发生内部错误!")
|
||||
case pytest.ExitCode.USAGE_ERROR:
|
||||
logging.error("Pytest 命令行参数错误或用法不当。")
|
||||
case pytest.ExitCode.NO_TESTS_COLLECTED:
|
||||
logging.warning("未发现任何测试用例。")
|
||||
case _:
|
||||
logging.error(f"未知错误状态码: {exitstatus}")
|
||||
|
||||
report_dir = session.config.getoption("--alluredir")
|
||||
if not report_dir:
|
||||
return
|
||||
report_path = Path(report_dir)
|
||||
|
||||
# 收集环境信息 (适配接口自动化)
|
||||
env_info = {
|
||||
"Base URL": base_url,
|
||||
"Environment": session.config.getoption("--env"),
|
||||
"Python Version": platform.python_version(),
|
||||
"OS System": platform.system(),
|
||||
"Project": "Interface Auto Test"
|
||||
}
|
||||
|
||||
try:
|
||||
if not report_path.exists():
|
||||
report_path.mkdir(parents=True, exist_ok=True)
|
||||
# 生成 environment.properties 文件
|
||||
env_file = report_path / "environment.properties"
|
||||
with env_file.open("w", encoding="utf-8") as f:
|
||||
for k, v in env_info.items():
|
||||
f.write(f"{k}={v}\n")
|
||||
logging.info("Allure 环境信息已生成。")
|
||||
except Exception as e:
|
||||
logging.error(f"无法写入环境属性: {e}")
|
||||
|
||||
Reference in New Issue
Block a user