You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 22:03:30