必须升级 pytest≥8.0.0、安装 pytest-asyncio≥0.23.0 并显式添加 @pytest.mark.asyncio 装饰器;否则 python 3.12 下因 collections.mapping 移除或 async 测试静默跳过等问题导致测试无法运行。

必须升级 pytest 到 8.0.0+,同时安装 pytest-asyncio ≥0.23.0,并显式加 @pytest.mark.asyncio 装饰器——三者缺一不可。
pytest 8.0.0 是 Python 3.12 的硬性门槛
Python 3.12 彻底移除了 collections.Mapping,而旧版 pytest(≤7.4.x)仍直接从 collections 导入该类,导致启动即崩:ImportError: cannot import name 'Mapping' from 'collections'。这不是警告,是 ImportError,pytest 命令根本跑不起来。
必须执行:pip install -U pytest>=8.0.0;仅 pip install --force-reinstall pytest 不会升主版本,容易卡在 7.x。
- 检查当前版本:
pip show pytest,确认输出里是Version: 8.x.x - 若被其他包锁住(如老版
pytest-cov),用pipdeptree | grep pytest查依赖链,针对性升级或替换 - Pytest 8.2+ 在 3.12 下可能报
DeprecationWarning: pkg_resources is deprecated,说明某个插件(如pytest-html)还在用旧 API,需单独升级该插件
@pytest.mark.asyncio 不是可选,是强制开关
即使装了 pytest-asyncio、配了 asyncio_mode = "auto",不加这个装饰器,async 测试函数仍会被静默跳过,或报 RuntimeWarning: coroutine 'test_xxx' was never awaited —— 表面“passed”,实际没执行。
原因在于:auto 模式只对模块级、命名规范(test_*.py 中的 async def test_*)生效;一旦测试在 class 里、用了 fixture、或函数名不标准,就失效。
- 必须写成:
@pytest.mark.asyncio async def test_ai_inference(): result = await mock_ai_inference("hello") assert result["status"] == "success" - 别把
@pytest.mark.asyncio错贴到 fixture 上——它只作用于测试函数 - 如果用了
event_loop这类 fixture,不加 mark 会导致 loop 注入失败,后续await直接报Event loop is closed
参数化测试中 None 返回值在 3.12 下立刻暴露
Python 3.12 对 @pytest.mark.parametrize 的参数可迭代性校验更严格。常见错误:@pytest.mark.parametrize("x", some_func()),而 some_func() 在某些分支返回 None(比如未 catch 异常、漏写 return)。
3.11 可能“容忍”这种写法,但 3.12 解析标记阶段就报:TypeError: 'NoneType' object is not iterable。
- 修复方式不是降级,而是补逻辑:
return [] if result is None else result - 临时排查可在
some_func()结尾加:assert isinstance(result, (list, tuple, types.GeneratorType)) - 别依赖
pytest-asyncio插件来绕过这个——这是你测试数据生成逻辑本身的缺陷
最容易被忽略的是:async fixture 不能裸 return 协程对象。比如 @pytest.fixture 函数里写 return fetch_user_async(),返回的是 <class></class>,下游测试一 await 就崩。必须确保 fixture 本身是同步的,或改用 async def fixture() + 配合 @pytest.mark.asyncio 测试函数驱动——但后者不支持 session 级 scope。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











