pytest_sessionfinish是唯一能稳定获取全局测试数据的钩子,需配合pytest_runtest_makereport缓存call阶段report,再在session结束时统计并异步发送通知,同时通过环境变量控制开关、动态读取webhook地址、设置超时与headers规避常见网络问题。

为什么 pytest 的 hook 函数收不到测试结果?
因为 pytest_runtest_makereport 本身不直接暴露最终汇总结果,它只在每个用例执行后触发一次,且返回的 report 对象是单个用例状态(passed/failed/skipped),没有全局统计信息。想发飞书/钉钉通知,必须等所有用例跑完、拿到总耗时、成功率、失败详情——这得靠 pytest_terminal_summary 或更稳妥的 pytest_sessionfinish。
怎么在 session 结束时拿到完整测试数据?
pytest_sessionfinish 是唯一能稳定访问全局结果的地方,它接收 session 对象,而 session.testscollected 和 session.testsfailed 等属性已就绪。但注意:这些只是计数,要获取具体失败用例的 traceback 和日志,得自己缓存——通常在 pytest_runtest_makereport 里提前收集。
- 在
conftest.py中定义全局列表all_reports = [] - 在
pytest_runtest_makereport里把report追加进去(仅当report.when == "call",避免 setup/teardown 重复记录) - 在
pytest_sessionfinish中遍历all_reports,筛选出failed或error条目,提取report.longreprtext或report.caplog
发飞书/钉钉前必须绕开哪些坑?
飞书和钉钉 Webhook 都要求 POST JSON,但默认超时短、不校验 SSL、不带 User-Agent,容易被拦截或静默失败。
- 用
requests.post(..., timeout=10, verify=False)避免证书问题(内网环境常见) - 必须设置
headers={"Content-Type": "application/json"},否则钉钉返回415 Unsupported Media Type - 飞书消息体字段名严格:
msg_type="post"+content下嵌套zh_cn对象;钉钉则是msgtype="text"或"markdown",且text.content不能含控制字符 - 别在
pytest_sessionfinish里直接调用阻塞式 HTTP 请求——万一网络卡住,整个 pytest 会 hang 住;建议用threading.Thread(target=send_notify).start()异步发
如何让 hook 支持多环境配置(本地/CI)?
本地开发时不该每跑一次都发消息,CI 流水线才需要。靠环境变量开关最轻量。
- 在
pytest.ini或pyproject.toml中加addopts = --webhook-env=ci,再通过parser.addoption注册自定义参数 - 在
pytest_sessionfinish开头加判断:if config.getoption("--webhook-env") != "ci": return - Webhook URL 别硬编码,从
os.getenv("FEISHU_WEBHOOK")或config.getini("feishu_webhook")读取,CI 中用 secret 注入
真正难的不是发消息,而是确保失败用例的堆栈截断合理、日志去敏(比如过滤掉密码字段)、以及重试机制——这些得你自己补,pytest hook 不管。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











