如何使用 pytest xfail 断言异常消息(异常类型断言有效)

And*_*iuc 6 python testing pytest

假设我有一个文件 test_scratch_2.py


import pytest


def my_fun(number: int):
    if number == 1:
        raise ValueError("Some Message for number 1")

    if number == 2:
        raise ValueError("Some Message for number 2")

    if number == 3:
        return int("number")

    return number ** 2


@pytest.mark.parametrize("digit", [
    # example 1
    pytest.param(1, id="first",
                 marks=pytest.mark.xfail(raises=ValueError,
                                         strict=True,
                                         reason="Some Message for number 1")),
    # example 2
    pytest.param(2, id="second",
                 marks=pytest.mark.xfail(raises=ValueError,
                                         strict=True,
                                         reason="Some Message for number 1")),
    pytest.param(3, id="third",
                 marks=pytest.mark.xfail(raises=ValueError,
                                         strict=True,
                                         reason="Some Message for number xxxxxxxxx")),

    4,
    5,
    60000
])
def test_my_fun(digit):
    assert my_fun(digit) > 0
Run Code Online (Sandbox Code Playgroud)

我运行测试


import pytest


def my_fun(number: int):
    if number == 1:
        raise ValueError("Some Message for number 1")

    if number == 2:
        raise ValueError("Some Message for number 2")

    if number == 3:
        return int("number")

    return number ** 2


@pytest.mark.parametrize("digit", [
    # example 1
    pytest.param(1, id="first",
                 marks=pytest.mark.xfail(raises=ValueError,
                                         strict=True,
                                         reason="Some Message for number 1")),
    # example 2
    pytest.param(2, id="second",
                 marks=pytest.mark.xfail(raises=ValueError,
                                         strict=True,
                                         reason="Some Message for number 1")),
    pytest.param(3, id="third",
                 marks=pytest.mark.xfail(raises=ValueError,
                                         strict=True,
                                         reason="Some Message for number xxxxxxxxx")),

    4,
    5,
    60000
])
def test_my_fun(digit):
    assert my_fun(digit) > 0
Run Code Online (Sandbox Code Playgroud)

在 的情况下存在错误test_scratch_2.py::test_my_fun[third] XFAIL

它会因预期的异常而失败,但不会因预期的原因而失败。

所有断言都基于异常类型而不是异常消息。

我如何查看异常消息?

Sim*_*vid 2

You can use pytest's with pytest.raises(ErrorWhichMustBeRaised, match='Error message which must appear'): context manager to achieve the desired behaviour, as suggested in this SO answer to a similar question.

To make this work, I divided your tests into "happy path" tests and "exceptions" tests. Please note that your use of xfail is technically an anti-pattern: "It is better to use the pytest.mark.xfail marker when possible to declare a test to be xfailed under certain conditions like known bugs or missing features." (source: official doc)

Testing the code below (in quiet mode) returns the error message you would want in your case:

============================================================== short test summary info ===============================================================
FAILED main.py::test_my_fun_exceptions[second] - AssertionError: Regex pattern did not match.
FAILED main.py::test_my_fun_exceptions[third] - AssertionError: Regex pattern did not match.
2 failed, 4 passed in 0.11s
Run Code Online (Sandbox Code Playgroud)

Please note that id='third' fails as well. That's because attempting to convert a string to an integer will raise a ValueError with a different error message than the one which appears in the code.

Code (python 3.11.4):

import pytest


def my_fun(number: int):
    if number == 1:
        raise ValueError("Some Message for number 1")

    if number == 2:
        raise ValueError("Some Message for number 2")

    if number == 3:
        return int("number")

    return number**2


@pytest.mark.parametrize("digit", [4, 5, 60000])
def test_my_fun(digit):
    assert my_fun(digit) > 0


@pytest.mark.parametrize(
    "digit, error, match_str",
    [
        # example 1
        (pytest.param(1, ValueError, "Some Message for number 1", id="first")),
        # example 2
        (pytest.param(2, ValueError, "Some Message for number 1", id="second")
         ),
        # example 3
        (pytest.param(
            3, ValueError, "Some Message for number xxxxxxxxx", id="third"))
    ])
def test_my_fun_exceptions(digit, error, match_str):
    with pytest.raises(error, match=match_str):
        assert my_fun(digit) > 0
Run Code Online (Sandbox Code Playgroud)