140 lines
4.8 KiB
Python
140 lines
4.8 KiB
Python
#!/usr/bin/env python
|
||
# coding=utf-8
|
||
|
||
"""
|
||
@desc: Pytest 配置文件,用于设置全局 Fixture 和钩子函数
|
||
"""
|
||
import platform
|
||
from typing import Any
|
||
|
||
import pytest
|
||
from pathlib import Path
|
||
import logging
|
||
|
||
from core.context import VariableStore, ExecutionEnv
|
||
from core.session import Session
|
||
from core.exchange import Exchange
|
||
from core.settings import EXTRACT_CACHE,base_url
|
||
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
# 注册命令行参数
|
||
def pytest_addoption(parser: Any) -> None:
|
||
"""
|
||
注册自定义命令行参数。
|
||
|
||
允许用户通过命令行传递参数来控制测试执行的行为。
|
||
|
||
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(EXTRACT_CACHE)
|
||
exchanger = Exchange(variable_cache=store.store)
|
||
session = Session(base_url)
|
||
|
||
env = ExecutionEnv(session, store, exchanger)
|
||
|
||
yield env # 注入到测试用例中
|
||
|
||
# Teardown: 统一持久化与清理
|
||
store.persist()
|
||
session.close()
|
||
|
||
|
||
def pytest_exception_interact(node: Any, call: Any, report: Any) -> None:
|
||
"""
|
||
[Hook] 异常交互钩子。
|
||
|
||
当测试用例执行失败(断言错误或代码异常)时触发。
|
||
主要用于捕获详细的错误堆栈信息,并将其格式化输出到日志中,
|
||
以便于在控制台或日志文件中快速定位问题。
|
||
|
||
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}")
|