pytest6.2.5运行报错Session对象无results属性是什么原因
问题原因
- 最常见诱因是启用了
pytest-xdist分布式执行插件:xdist会启动多个独立的worker进程,每个进程持有独立的Session实例。你在主进程pytest_sessionstart钩子中为Session添加的results属性无法同步到worker进程,worker执行测试时访问item.session.results就会触发属性不存在的报错。 - conftest.py加载范围异常:如果你的conftest.py没有放在pytest执行命令的根目录,或者测试用例的顶层目录下,
pytest_sessionstart钩子不会被pytest加载执行,自然不会初始化Session的results属性。 - 第三方插件抢占hook执行:如果有其他插件也实现了
pytest_sessionstart钩子且优先级更高,可能导致你的钩子逻辑被覆盖、执行中断,results属性没有被初始化。
解决方案
方案1:验证基础配置
先在你的pytest_sessionstart函数中添加打印日志,运行pytest时检查是否有对应的输出:
- 没有输出:说明钩子未被加载,将conftest.py移动到pytest执行命令的根目录下即可。
- 有输出:说明是xdist或者属性冲突导致的问题,继续看下面的方案。
方案2:兼容所有场景的代码修改
使用pytest 6.2+内置的stash存储全局数据,避免直接给Session对象动态加属性导致的冲突,修改后的conftest.py代码如下:
import pytest # 定义唯一的stash键,避免和其他插件的存储冲突 RESULTS_STASH_KEY = pytest.StashKey[dict]() def pytest_sessionstart(session): print("session start") session.stash[RESULTS_STASH_KEY] = dict() @pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_runtest_makereport(item, call): outcome = yield result = outcome.get_result() if result.when == "call": item.session.stash[RESULTS_STASH_KEY][item] = result @pytest.hookimpl(trylast=True) def pytest_sessionfinish(session, exitstatus): print() print("run status code:", exitstatus) results = session.stash[RESULTS_STASH_KEY] passed_amount = sum(1 for res in results.values() if res.passed) failed_amount = sum(1 for res in results.values() if res.failed) print(f"passed: {passed_amount}, failed: {failed_amount}")
方案3:如果你使用了pytest-xdist
如果需要保留分布式执行能力,除了上面的代码修改,还需要额外实现xdist的结果同步逻辑,在conftest.py中新增以下钩子即可:
def pytest_configure(config): # 适配xdist,worker进程不需要处理最终汇总 if hasattr(config, "workerinput"): @pytest.hookimpl(tryfirst=True) def pytest_sessionfinish(session, exitstatus): # 将worker的结果回传给主进程 config.workeroutput["results"] = session.stash[RESULTS_STASH_KEY] def pytest_sessionfinish(session, exitstatus): if not hasattr(session.config, "workerinput"): # 主进程汇总所有worker的结果 if hasattr(session.config, "workeroutput"): all_results = {} for worker_out in session.config.workeroutput.values(): all_results.update(worker_out.get("results", {})) session.stash[RESULTS_STASH_KEY] = all_results # 后续执行你的自定义逻辑即可 results = session.stash[RESULTS_STASH_KEY] passed_amount = sum(1 for res in results.values() if res.passed) failed_amount = sum(1 for res in results.values() if res.failed) print(f"total passed: {passed_amount}, total failed: {failed_amount}")
内容的提问来源于stack exchange,提问作者yyuankm
相关产品推荐
相关产品推荐

