#!/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}")