如何用Pytest断言由指定异常引发的原始异常?
如何用pytest捕获并匹配被链式抛出的原始异常消息
问题背景
你按照Stack Overflow教程及官方文档编写断言异常的测试套件,但遇到一个场景:需要从想要断言的异常引出新异常,现在需要用pytest正确捕获并匹配原始异常的消息。以下是可复现代码:
待测试代码(src/main.py)
# in src/main.py # this is not exactly like that in the code, but it is what s equivalent class ModelValidationError(Exception): def __init__(self, *_): super().__init__( f"Unable to initialize model" ) class InvalidParametersWarning(Warning): def __init__(self, *_): super().__init__( "The given parameters are invalid" ) def handle_model_validation_error(environment: str, exception: ValueError): """Raise the correct exception based on the environment we are in. To avoid frightening the users, we raise a warning in production environments and an exception in development environments. Parameters ---------- environment : str in which application environment we are in. exception : ValueError Raises ------ ModelValidationError For development environments. Is is raised from the ValueError. InvalidParametersWarning For production environments. """ if environment == "development": raise ModelValidationError() from exception else: raise InvalidParametersWarning()
原测试代码(存在问题)
import pytest from src.main import handle_model_validation_error def test_exception(): # 想要匹配ValueError的消息"nope",该如何实现? with pytest.raises(ValueError, match="nope"): handle_model_validation_error("development", ValueError("nope"))
简化等价示例
import pytest def test_exception(): # 想要匹配ValueError的消息"nope",该如何实现? with pytest.raises(ValueError, match="nope"): raise Exception from ValueError("nope")
解决方案
当使用raise NewException() from OriginalException抛出链式异常时,pytest的pytest.raises无法直接捕获原始异常——因为实际抛出的是新异常。此时需要先捕获新异常,再通过其__cause__属性访问原始异常,进而验证消息。
方法一:捕获新异常后手动断言原始异常
修改测试代码,先捕获抛出的ModelValidationError,再检查它的__cause__属性:
import pytest from src.main import handle_model_validation_error, ModelValidationError def test_exception_with_cause(): with pytest.raises(ModelValidationError) as exc_info: handle_model_validation_error("development", ValueError("nope")) # 验证原始异常的类型 assert isinstance(exc_info.value.__cause__, ValueError) # 验证原始异常的消息 assert exc_info.value.__cause__.args[0] == "nope"
方法二:匹配原始异常的消息内容
如果只需要验证消息包含指定字符串,也可以这样写:
def test_exception_match_cause_message(): with pytest.raises(ModelValidationError) as exc_info: handle_model_validation_error("development", ValueError("nope")) assert exc_info.value.__cause__ is not None assert "nope" in str(exc_info.value.__cause__)
简化示例的修改版本
针对简化示例,测试代码修改如下:
import pytest def test_exception(): with pytest.raises(Exception) as exc_info: raise Exception from ValueError("nope") assert isinstance(exc_info.value.__cause__, ValueError) assert exc_info.value.__cause__.args[0] == "nope"
关键说明
exc_info.value是pytest捕获到的新异常实例(如ModelValidationError)- Python链式异常中,
__cause__属性专门存储通过from关键字关联的原始异常 - 可以通过
__cause__.args[0]直接获取原始异常的消息,或用str(__cause__)转为字符串后做模糊匹配
内容的提问来源于stack exchange,提问作者Marco Bresson
相关产品推荐
相关产品推荐

