最直接方式是用unittest.TestCase.assertRaises或上下文管理器捕获异常并验证类型及属性,pytest.raises提供更灵活的消息匹配和异常实例访问,需注意异常传播路径和继承关系。

用 assertRaises 捕获并验证异常类型
最直接的方式是使用 unittest.TestCase.assertRaises,它会在上下文执行期间等待指定异常出现,没抛出就报错,抛出别的异常也会失败。
注意:必须传入异常类(如 ValueError),不能传字符串或实例;且被测代码不能提前被调用(即不能写成 assertRaises(ValueError, func()),括号会导致立即执行)。
- 正确写法是
self.assertRaises(ValueError, int, "abc")—— 把函数和参数分开传 - 推荐用上下文管理器形式,更清晰、还能拿到异常实例:
with self.assertRaises(TypeError) as cm: len(42) self.assertEqual(str(cm.exception), "object of type 'int' has no len()") - 如果被测逻辑需要 setup/teardown 或涉及状态变更,上下文方式更容易控制边界
当需要检查异常的 args 或属性时
assertRaises 本身不校验异常消息,但通过上下文管理器拿到的 cm.exception 是真实异常对象,可对其任意属性做断言。
常见需求包括验证错误消息是否包含关键词、是否匹配正则、或者是否设置了特定字段(比如自定义异常里的 .code 属性)。
- 检查消息子串:
self.assertIn("timeout", str(cm.exception)) - 检查完整消息(注意换行和空格):
self.assertEqual(cm.exception.args[0], "invalid mode: 'z'") - 检查自定义属性:
self.assertEqual(cm.exception.code, 400)
用 pytest.raises 替代(非 unittest 场景)
如果你用的是 pytest,pytest.raises 更灵活,支持匹配异常类型、消息正则、甚至允许“不抛异常时通过”(加 match= 参数自动校验消息)。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
它返回一个异常捕获对象,也能像 unittest 那样取 .value 做深度断言,但语法更贴近自然表达。
- 基础用法:
with pytest.raises(ZeroDivisionError): 1 / 0 - 带消息校验:
with pytest.raises(ValueError, match=r"must be \d+ or \d+"): parse_version("x.y") - 获取异常实例:
with pytest.raises(RuntimeError) as excinfo: risky_call() assert "network" in str(excinfo.value)
容易忽略的陷阱:异常未被触发、被外层吞掉、或类型不匹配
最常见的失败不是代码写错,而是测试环境干扰了异常传播路径。比如被 try...except 吞掉、日志模块把异常转成 warning、或者 mock 覆盖了真实调用链。
另一个高频问题是异常继承关系没理清:比如函数实际抛 ConnectionRefusedError(它是 OSError 的子类),但你断言的是 OSError —— 这能过;反过来断言子类却遇到父类,则失败。
- 确认异常确实从被测函数内部抛出,而不是在 setUp 或 fixture 里
- 用
python -m unittest -v your_test看详细输出,有时失败信息里会显示“ExpectedValueErrorbut nothing raised” - 不确定具体类型时,先运行一次不加断言的代码,用
except Exception as e: print(type(e).__name__, e)快速确认
测试里异常断言看着简单,但一旦涉及多层封装、异步调用或第三方库拦截,传播路径就容易变模糊。动手前先确保你能手动复现那个异常,并观察它到底在哪儿、以什么类型冒出来。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










