"""根级 conftest — 全局 fixture:配置加载、日志、中文 HTML 报告生成。 Usage: def test_xxx(admin_config): # 管理端配置 def test_xxx(driver_android_config): # 司机安卓配置 """ from pathlib import Path import sys import pytest sys.path.insert(0, str(Path(__file__).parent.parent)) @pytest.fixture(scope="session") def config(): """全局配置(session 级,只加载一次)。""" from tests.config.test_config import load_config return load_config() @pytest.fixture(scope="function") def logger(request): """每个测试函数的 logger。""" from tests.utils.logger import get_logger module_name = request.module.__name__.replace("tests.", "") return get_logger(module_name) # ═══════════════════════════════════════════════════════════════════ # 各端配置 fixture(从 config 中摘取对应 section) # ═══════════════════════════════════════════════════════════════════ @pytest.fixture(scope="session") def admin_config(config): """管理端 (Playwright) 配置。""" return config["admin"] @pytest.fixture(scope="session") def mini_program_config(config): """司机端小程序 (Playwright) 配置。""" return config["mini_program"] @pytest.fixture(scope="session") def driver_android_config(config): """司机安卓端 (Appium) 配置。""" return config["driver_android"] @pytest.fixture(scope="session") def shipper_android_config(config): """货主安卓端 (Appium) 配置。""" return config["shipper_android"] @pytest.fixture(scope="session") def driver_ios_config(config): """司机苹果端 (Appium) 配置。""" return config["driver_ios"] @pytest.fixture(scope="session") def shipper_ios_config(config): """货主苹果端 (Appium) 配置。""" return config["shipper_ios"] # ═══════════════════════════════════════════════════════════════════ # 中文 HTML 报告生成 (pytest_sessionfinish hook) # ═══════════════════════════════════════════════════════════════════ def pytest_sessionfinish(session): """测试会话结束后,从 junit.xml 生成一份简洁的中文 HTML 报告。""" from datetime import datetime from xml.etree import ElementTree as ET report_dir = Path("output/reports") report_dir.mkdir(parents=True, exist_ok=True) junit_path = report_dir / "junit.xml" if not junit_path.exists(): return try: tree = ET.parse(junit_path) root = tree.getroot() except Exception: return total = 0 passed = 0 failed = 0 skipped = 0 cases = [] for suite in root: for case in suite: total += 1 name = case.attrib.get("name", "") time_elapsed = case.attrib.get("time", "0") failure = case.find("failure") if failure is not None: failed += 1 msg = failure.attrib.get("message", "")[:300] cases.append((name, "失败", time_elapsed, msg)) elif case.find("skipped") is not None: skipped += 1 cases.append((name, "跳过", time_elapsed, "")) else: passed += 1 cases.append((name, "通过", time_elapsed, "")) pass_rate = (passed / total * 100) if total > 0 else 0 now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") html = _build_cn_report_html(total, passed, failed, skipped, pass_rate, cases, now) (report_dir / "report_cn.html").write_text(html, encoding="utf-8") def _build_cn_report_html(total, passed, failed, skipped, rate, cases, now): """拼装中文 HTML 报告字符串。""" rows = "" for i, (name, status, elapsed, msg) in enumerate(cases, 1): cls = {"通过": "s-pass", "失败": "s-fail", "跳过": "s-skip"}.get(status, "") msg_html = f'{msg}' if msg else "-" rows += f"
| # | 用例名称 | 状态 | 耗时(s) | 备注 |
|---|