Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use pytest.raises with multiple exceptions?

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.

like image 728
fenceop Avatar asked Jul 15 '16 19:07

fenceop


People also ask

Can you raise multiple exceptions?

Strictly speaking you can't raise multiple exceptions but you could raise an object that contains multiple exceptions.

What is pytest Assertionerror?

Pytest, by way of magic (also known as introspection) can infere the actual value, the expected value, and the operation used in a plain old assert statement and can provide a rather nice error message.


1 Answers

Pass the exceptions as a tuple to raises:

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

In the source for pytest, pytest.raises may:

  • catch expected_exception; or
  • pass expected_exception to a RaisesContext instance, which then uses issubclass to check whether the exception was one you wanted.

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.

like image 93
cxw Avatar answered Oct 19 '22 04:10

cxw