应使用 self.assertraises(baseexceptiongroup) 或 try/except 显式检查,因 assertraises(exceptiongroup) 失败;pytest.raises 需用 baseexceptiongroup 且注意版本兼容性;并发测试需避免 asyncio.run() 封装。

如何用 unittest 捕获 BaseExceptionGroup
Python 3.11 引入了 BaseExceptionGroup 和 ExceptionGroup,但 unittest.TestCase.assertRaises 默认不识别它们——直接写 self.assertRaises(ExceptionGroup, ...) 会失败,因为底层用的是 isinstance(exc, expected) 判断,而 ExceptionGroup 是 BaseExceptionGroup 的子类,不是 Exception 子类,所以常规 assertRaises 会跳过。
正确做法是改用 assertRaises 的底层机制:手动触发并检查类型:
- 用
with self.assertRaises(BaseExceptionGroup) as cm:—— 这能捕获,但注意:它只认BaseExceptionGroup及其子类,不能写ExceptionGroup(除非你明确知道抛出的是ExceptionGroup) - 更稳妥的方式是用
try/except+isinstance显式判断:try: some_async_code() except BaseExceptionGroup as eg: self.assertIsInstance(eg, BaseExceptionGroup) self.assertEqual(len(eg.exceptions), 2) - 别依赖
assertRaisesRegex—— 它内部也走isinstance路径,对BaseExceptionGroup不生效
测试 ExceptionGroup 里嵌套的异常类型
ExceptionGroup 本身不继承 Exception,但它允许包裹多个 Exception 实例。测试时容易误以为 eg.exceptions[0] 是原始异常,其实它可能是被封装过的副本,尤其在 asyncio.gather(return_exceptions=True) 场景下。
关键点:
- 用
isinstance(eg.exceptions[0], ValueError)是安全的,但不要假设它是原对象(is比较大概率失败) - 若需验证消息内容,应遍历
eg.exceptions并逐个检查:for exc in eg.exceptions: if isinstance(exc, ValueError): self.assertIn("timeout", str(exc)) - 注意
eg.derive()返回新组,但不会改变原组结构;测试中避免误用derive当作“提取”手段
pytest 中怎么写断言才不报错
pytest 的 pytest.raises 在 7.2+ 已支持 BaseExceptionGroup,但必须显式传入 match 或用上下文管理器方式,否则可能静默失败。
Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。
常见陷阱:
- 写成
with pytest.raises(ExceptionGroup):—— 如果实际抛出的是BaseExceptionGroup(比如空组),会失败;应统一用BaseExceptionGroup - 使用
match时,匹配的是整个组的字符串表示(即str(eg)),不是单个异常的消息,例如:with pytest.raises(BaseExceptionGroup, match=r"2 sub-exceptions"): raise ExceptionGroup("oops", [ValueError("bad"), TypeError("nope")]) - pytest 7.1 之前版本不支持
BaseExceptionGroup,升级前先确认pip show pytest版本
并发场景下 assertRaises 容易漏掉异常组
在 asyncio 测试中,比如用 asyncio.run() 执行一个会触发 ExceptionGroup 的协程,直接套 assertRaises 往往捕获不到——因为异常是在事件循环内部被包装成 BaseExceptionGroup 后再抛出,而 assertRaises 只监听顶层调用栈。
推荐做法:
- 不用
asyncio.run()包裹待测协程,改用asyncio.Runner().run()或手动创建 loop(Python 3.11+ 推荐前者) - 更可靠的是在协程内主动
raise,或用asyncio.create_task()+task.exception()检查返回值 - 记住:一个
ExceptionGroup可能包含CancelledError和业务异常,测试时别只检查len(eg.exceptions) == 1,要确认每个成员类型
最常被忽略的是:BaseExceptionGroup 的构造参数是 (message, exceptions),其中 exceptions 必须是序列,传入单个异常会变成 (message, (exc,)),但很多人误写成 ExceptionGroup("msg", exc) 导致运行时报 TypeError —— 这类错误在测试里反而不容易暴露,得靠代码审查或类型检查工具提前发现。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










