pytest 7.4+ 才支持 ExceptionGroup 原生断言,此前版本完全不识别;需升级至 ≥7.4.0,捕获顶层 ExceptionGroup 本身,协程中须 await 或用 @pytest.mark.asyncio。

pytest 7.4+ 才支持 ExceptionGroup 的原生断言
Python 3.11 引入了 ExceptionGroup 和 BaseExceptionGroup,但 pytest 在 7.4 之前完全不识别它们——pytest.raises(ValueError) 会直接忽略嵌套在 ExceptionGroup 中的 ValueError,导致断言失败或静默跳过。升级到 pytest>=7.4.0 是前提,否则所有尝试都无效。
验证方式:
pip show pytest确保输出中
Version: ≥ 7.4.0;若低于该版本,pip install -U pytest 升级。
用 pytest.raises() 捕获顶层 ExceptionGroup 本身
当被测代码显式抛出 ExceptionGroup(例如 raise ExceptionGroup("eg", [ValueError("a"), TypeError("b")])),需直接匹配该组类型和消息:
-
pytest.raises(ExceptionGroup, match="eg")可捕获并验证组名 - 不能写成
pytest.raises(ValueError)—— 这只会检查最外层异常是否为ValueError,而实际抛出的是ExceptionGroup - 若需检查组内异常类型构成,得在
with块中手动展开:with pytest.raises(ExceptionGroup) as exc_info: raise ExceptionGroup("eg", [ValueError("a"), TypeError("b")]) assert len(exc_info.value.exceptions) == 2 assert isinstance(exc_info.value.exceptions[0], ValueError)
用 match 参数定位组内特定异常(pytest 7.4.2+)
pytest 7.4.2 起支持对 ExceptionGroup 内部异常做子匹配,语法是 match 值中包含 ^ 分隔符,格式为:"group_message^exception_type:message_pattern":
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
立即学习“Python免费学习笔记(深入)”;
-
pytest.raises(ExceptionGroup, match="eg^ValueError:a")表示:顶层组名为eg,且组内至少有一个ValueError其字符串表示含a - 多个条件用
|分隔:match="eg^ValueError:a|TypeError:b" - 注意:
match匹配的是整个异常对象的repr()或str()输出,不是仅消息文本;若异常未重写__str__,实际匹配的是类似ValueError('a')这样的字符串 - 不支持嵌套层级超过一层的深度匹配(例如组中再套组)
常见陷阱:协程、asyncio.gather 与 ExceptionGroup 的交互
在 asyncio.gather(..., return_exceptions=False)(默认行为)下,并发任务中任一异常会触发 ExceptionGroup 抛出,但 pytest 的 raises 默认不进入协程上下文:
- 直接写
pytest.raises(ExceptionGroup)在普通测试函数里会报Failed: DID NOT RAISE,因为协程未 await - 必须用
async def test_xxx()+await,或用pytest.mark.asyncio(需装pytest-asyncio) - 错误示例:
def test_bad(): with pytest.raises(ExceptionGroup): # ❌ 不会触发 asyncio.gather(coro_a(), coro_b()) # 返回协程对象,未执行 - 正确写法:
import pytest @pytest.mark.asyncio async def test_good(): with pytest.raises(ExceptionGroup): await asyncio.gather(coro_a(), coro_b())
协程场景下,match 子匹配仍有效,但要确保 await 后异常真正抛出,而不是被 return_exceptions=True 吞掉。

















