From f287c93cce2b725fb58f77cbd1b2a6ae3c16c6b6 Mon Sep 17 00:00:00 2001 From: xst Date: Thu, 23 Jul 2026 16:42:59 +0800 Subject: [PATCH] feat(report): auto-generate Chinese HTML report after test session via pytest_sessionfinish hook Produces output/reports/report_cn.html alongside junit.xml and report.html --- tests/conftest.py | 120 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 757af34..f4c6c04 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,4 @@ -"""根级 conftest — 全局 fixture:配置加载、日志。 +"""根级 conftest — 全局 fixture:配置加载、日志、中文 HTML 报告生成。 Usage: def test_xxx(admin_config): # 管理端配置 @@ -37,27 +37,145 @@ 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"{i}{name}{status}" + rows += f"{float(elapsed):.1f}{msg_html}" + + return f""" + + + +自动化测试报告 - {now} + + + +
+

自动化测试报告

+
执行时间: {now} | 运八网络货运平台 QaAutomationHub
+
+
+
{total}
总计
+
{passed}
通过
+
{failed}
失败
+ +
+
通过率: {rate:.1f}% ({passed}/{total})
+
+ +{rows} +
#用例名称状态耗时(s)备注
+
报告自动生成于 {now}
+"""