pytest.raises 是捕获并验证异常的最稳妥方式,需置于 with 语句块内,支持类型匹配、正则校验消息及属性断言;不支持块外访问返回值或变量,不自动匹配父类异常,不捕获被吞掉的异常或 sys.exit()。

用 pytest.raises 捕获并验证异常类型和消息
直接用 pytest.raises 是最稳妥的方式,它能精确捕获指定异常,并支持检查异常消息、属性,还能防止测试因未抛出异常而意外通过。
常见错误是写成 assert raise ValueError 或用 try/except 手动捕获后断言——这两种方式都无法保证“代码确实抛出了异常”,容易漏掉静默失败的 case。
- 必须把待测代码放在
with pytest.raises(...)语句块内,不能提前调用 - 若只关心异常类型,传入类名即可:
with pytest.raises(ValueError): func() - 若要校验异常消息,用
match参数(正则匹配):with pytest.raises(ValueError, match=r"must be positive"): -
pytest.raises返回一个异常对象,可进一步断言其属性:with pytest.raises(KeyError) as exc_info: d["missing"] assert "missing" in str(exc_info.value)
当函数返回值和异常共存时,别误用 return 值做断言
有些函数在出错时既抛异常又返回值(比如部分 C 扩展或带 fallback 的封装),但 pytest.raises 会吞掉返回值——你无法在 with 块外访问它。这时候不能靠“返回值是否为 None”来间接推断异常是否发生。
- 所有逻辑判断必须在
with pytest.raises(...)内完成,或通过exc_info提取异常上下文 - 如果函数本应无返回值(
None),不要写assert func() is None来代替异常断言——这完全绕过了异常检测 - 对多分支逻辑,确保测试覆盖了明确触发异常的输入路径,而不是依赖默认 fallback 行为
注意上下文管理器作用域与变量可见性
pytest.raises 是上下文管理器,其作用域严格限于 with 块内部。块外无法访问被测函数的局部变量,也不能复用块内定义的变量做后续断言。
- 需要检查异常前的状态?把断言提前到
with块之前,或改用try/except(仅当必须且可控时) - 想验证异常发生后某对象状态是否改变?确保该对象在
with块外可访问,且修改发生在异常抛出前(否则可能根本执行不到) - 避免在
with块里赋值新变量然后指望外面能用——Python 作用域规则不会因为 pytest 而改变
自定义异常或继承链中的匹配行为
pytest.raises 默认匹配精确类型,不自动向上匹配父类,除非显式传入父类。这对自定义异常设计很关键。
- 若函数抛出
MyValidationError(继承ValueError),而你写pytest.raises(ValueError),它能捕获;但反过来不行 - 多个可能异常?用元组:
pytest.raises((ValueError, TypeError)),但无法分别断言各自消息 - 需要区分不同子类异常?必须逐个写独立测试用例,或在
match中加入足够区分度的文本特征 - 注意
BaseException和Exception的区别:捕获BaseException会包括SystemExit、KeyboardInterrupt,通常不该这么做
pytest.raises 不会捕获未被 raise 出来的异常(比如被 except 吞掉但没 re-raise),也不会检测 sys.exit() 这类进程级终止。如果你的代码走到了那里,得换用 capsys 或 monkeypatch 配合测试。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











