Python 3.12 下 pytest 无语法新特性,但需升级至 pytest 7.4.0+(推荐 8.2+)并适配 async fixture、__getattr__ 行为及 pytest-asyncio 0.23.0+,否则出现静默跳过或 ImportError。

Python 3.12 中 pytest 本身没有语法级新特性,但运行时行为和兼容性有细微变化——关键不是“怎么写新东西”,而是“别踩 3.12 引入的隐性坑”。比如 async fixture 支持更严格、__getattr__ 在测试类中触发方式变了,老写法可能静默失效。
pytest 7.4+ 在 Python 3.12 下必须升级到的最低版本
低于 pytest 7.4.0 的版本在 Python 3.12 上会报 ImportError: cannot import name 'Mapping' from 'collections'——因为 Python 3.12 移除了 collections.Mapping(改用 collections.abc.Mapping)。这不是你代码的问题,是 pytest 自身依赖没适配。
- 执行
pip install "pytest>=7.4.0",推荐直接上pytest 8.2+ - 检查版本:
python -m pytest --version,确认输出含pytest 8.x或7.4.x - 如果用了
pytest-asyncio,也得同步升到0.23.0+,否则@pytest.mark.asyncio会跳过或卡住
async def 测试函数必须显式标记 @pytest.mark.asyncio
Python 3.12 对协程对象的类型检查更严,pytest 不再自动识别 async def test_xxx() 为可运行测试。不加标记会导致测试被跳过,且无提示。
安全 Python 代码沙箱:在隔离的沙箱中执行任意 Python 代码,预装 requests、NumPy、pandas 库。返回 stdout、stderr。
- 错误写法:
async def test_fetch_data(): ...→ 运行时完全不执行 - 正确写法:
@pytest.mark.asyncio async def test_fetch_data(): assert await some_async_func() == "ok" - 如果整个文件都是 async 测试,可在
pytest.ini加:asyncio_mode = auto,但 Python 3.12 + pytest 8.2 起该配置已被弃用,不推荐依赖
避免在测试类里重写 __getattr__ 或 __getattribute__
Python 3.12 修改了描述符协议调用顺序,导致测试类中自定义的 __getattr__ 可能拦截 pytest 的内部属性访问(如 self.request、self.tmp_path),引发 AttributeError 或奇怪的 None 返回。
- 现象:使用
tmp_pathfixture 时突然报AttributeError: 'TestFoo' object has no attribute 'tmp_path' - 临时解法:把
__getattr__改成只处理明确前缀,例如:def __getattr__(self, name): if name.startswith("mock_"): return Mock() raise AttributeError(f"{type(self).__name__} has no attribute '{name}'") - 更稳妥做法:用独立的 helper 类封装动态属性逻辑,不要污染测试类本身
Python 3.12 的改动多数藏在底层,表面看测试照跑,但失败时错误信息更模糊、跳过更安静。重点盯住版本兼容性和 async 处理——其他写法和以前一样,只是容错率低了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










