use type error message in pytest parametrize

Question:

I have a function which raises a TypeError when some conditions are met.

def myfunc(..args here...):
    ... 
    raise TypeError('Message')

I want to test this message using pytest parametrize.

But, because I am using other arguments also I want to have a setup like this:

testdata = [
        (..args here..., 'Message'), # Message is the expected output
    ]

    @pytest.mark.parametrize(
        "..args here..., expected_output", testdata)
    def test_myfunc(
       ..args here..., expected_output):

        obs = myfunc()
        assert obs == expected_output

Simple putting the Message as the expected output in the parametrize testdata, gives me a failing test.

Asked By: George

||

Answers:

You can’t expect message error as a normal output of myfunc. There is a special context manager for this – pytest.raises.

For example, if you want to expect some error and its message

def test_raises():
    with pytest.raises(Exception) as excinfo:   
        raise Exception('some info')   
    assert str(excinfo.value) == 'some info'

So, in your case, this is going to be something like

testdata = [
    (..args here..., 'Message')
]

@pytest.mark.parametrize("..args here..., expected_exception_message", testdata)
    def test_myfunc(..args here..., expected_exception_message):
        with pytest.raises(TypeError) as excinfo: 
            obs = myfunc(..args here...)
        assert str(excinfo.value) == expected_exception_message
Answered By: Piotr Dawidiuk

The following is from the pytest docs here:

Parametrizing conditional raising

Use pytest.raises() with the pytest.mark.parametrize decorator to write parametrized tests in which some tests raise exceptions and others do not.

It may be helpful to use nullcontext as a complement to raises.

For example:

from contextlib import nullcontext as does_not_raise

import pytest


@pytest.mark.parametrize(
    "example_input,expectation",
    [
        (3, does_not_raise()),
        (2, does_not_raise()),
        (1, does_not_raise()),
        (0, pytest.raises(ZeroDivisionError)),
    ],
)
def test_division(example_input, expectation):
    """Test how much I know division."""
    with expectation:
        assert (6 / example_input) is not None

In the example above, the first three test cases should run unexceptionally, while the fourth should raise ZeroDivisionError.

But that didn’t quite work for me…

The example in the Pytest docs caused me to get the error AttributeError: __enter__.

It seems that my Python’s nullcontext doesn’t have an __enter__ method implemented. Therefore I had to create my own version like this:

class MyNullContext:
    def __enter__(self, *args, **kwargs):
        pass
    def __exit__(self, *args, **kwargs):
        pass
does_not_raise = MyNullContext()

and use that instead of importing the builtin nullcontext. You could throw that in a conftest.py file so that it’s available for all your tests.

Answered By: mareoraft
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.