How can I use pytest.raises with multiple exceptions?

fenceop picture fenceop · Jul 15, 2016 · Viewed 7.8k times · Source

I'm testing code where one of two exceptions can be raised: MachineError or NotImplementedError. I would like to use pytest.raises to make sure that at least one of them is raised when I run my test code, but it only seems to accept one exception type as an argument.

This is the signature for pytest.raises:

raises(expected_exception, *args, **kwargs)

I tried using the or keyword inside a context manager:

with pytest.raises(MachineError) or pytest.raises(NotImplementedError):
    verb = Verb("donner<IND><FUT><REL><SG><1>")
    verb.conjugate()

but I assume this only checks whether the first pytest.raises is None and sets the second one as the context manager if it is.

Passing multiple exceptions as positional arguments doesn't work, because pytest.raises takes its second argument to be a callable. Every subsequent positional argument is passed as an argument to that callable.

From the documentation:

>>> raises(ZeroDivisionError, lambda: 1/0)
<ExceptionInfo ...>

>>> def f(x): return 1/x
...
>>> raises(ZeroDivisionError, f, 0)
<ExceptionInfo ...>
>>> raises(ZeroDivisionError, f, x=0)
<ExceptionInfo ...>

Passing the exceptions as a list doesn't work either:

Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    with pytest.raises([MachineError, NotImplementedError]):
  File "/usr/local/lib/python3.4/dist-packages/_pytest/python.py", line 1290, in raises
    raise TypeError(msg % type(expected_exception))
TypeError: exceptions must be old-style classes or derived from BaseException, not <class 'list'>

Is there a workaround for this? It doesn't have to use a context manager.

Answer

cxw picture cxw · Jul 15, 2016

Pass the exceptions as a tuple to raises:

with pytest.raises( (MachineError, NotImplementedError) ):
    verb = ...

In the source for pytest, pytest.raises may:

In Python 3, except statements can take a tuple of exceptions. The issubclass function can also take a tuple. Therefore, using a tuple should be acceptable in either situation.