1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
Testing exceptions
==================
.. currentmodule:: testfixtures
Testfixtures has tools to help when making assertions about exceptions that should be raised by
a piece of code.
The :class:`ShouldRaise` context manager
----------------------------------------
The :class:`ShouldRaise` context manager is the recommended way to make assertions about
a piece of code that should raise exceptions.
Suppose we wanted to test the following function to make sure that the
right exception was raised:
.. code-block:: python
def the_thrower(throw=True):
if throw:
raise ValueError('Not good!')
The following example shows how to test that the correct exception is
raised:
>>> from testfixtures import ShouldRaise
>>> with ShouldRaise(ValueError('Not good!')):
... the_thrower()
If the exception raised doesn't match the one expected,
:class:`ShouldRaise` will raise an :class:`AssertionError`
causing the tests in which it occurs to fail:
>>> with ShouldRaise(ValueError('Is good!')):
... the_thrower()
Traceback (most recent call last):
...
AssertionError: ValueError('Is good!'...) (expected) != ValueError('Not good!'...) (raised)
If you're not concerned about anything more than the type of the
exception that's raised, you can check as follows:
>>> from testfixtures import ShouldRaise
>>> with ShouldRaise(ValueError):
... the_thrower()
If you're feeling slack and just want to check that an exception is
raised, but don't care about the type of that exception, the following
will suffice:
>>> from testfixtures import ShouldRaise
>>> with ShouldRaise():
... the_thrower()
If no exception is raised by the code under test, :class:`ShouldRaise`
will raise an :class:`AssertionError` to indicate this:
>>> from testfixtures import ShouldRaise
>>> with ShouldRaise():
... the_thrower(throw=False)
Traceback (most recent call last):
...
testfixtures.shouldraise.NoException: No exception raised!
:class:`ShouldRaise` has been implemented such that it can be
used to test code that raises all exceptions, including :class:`SystemExit` and
:class:`KeyboardInterrupt` exceptions.
To help with :class:`SystemExit` and other exceptions that are
tricky to construct yourself, :class:`ShouldRaise` instances have a
:attr:`~ShouldRaise.raised` attribute. This will contain the actual
exception raised and can be used to inspect parts of it:
>>> import sys
>>> from testfixtures import ShouldRaise
>>> with ShouldRaise() as s:
... sys.exit(42)
>>> s.raised.code
42
The :func:`should_raise` decorator
-----------------------------------------
If you are working in a traditional :mod:`unittest` environment and
want to check that a particular test function raises an exception, you
may find the decorator suits your needs better:
.. code-block:: python
from testfixtures import should_raise
@should_raise(ValueError('Not good!'))
def test_function():
the_thrower()
This decorator behaves exactly as the :class:`ShouldRaise` context
manager described in the documentation above.
.. note::
It is slightly recommended that you use the context manager rather
than the decorator in most cases. With the decorator, all exceptions
raised within the decorated function will be checked, which can
hinder test development. With the context manager, you can make
assertions about only the exact lines of code that you expect to
raise the exception.
Exceptions that are conditionally raised
----------------------------------------
Some exceptions are only raised in certain versions of Python. For
example, in Python 2, ``bytes()`` will turn both bytes and strings into
bytes, while in Python 3, it will raise an exception when presented
with a string. If you wish to make assertions that this behaviour is
expected, you can use the ``unless`` option to :class:`ShouldRaise`
as follows:
.. code-block:: python
import sys
from testfixtures import ShouldRaise
PY2 = sys.version_info[:2] < (3, 0)
with ShouldRaise(TypeError, unless=PY2):
bytes('something')
.. note::
Do **not** abuse this functionality to make sloppy assertions. It is
always better have two different tests that cover a case when an
exception should be raised and a case where an exception should not
be raised rather than using it above functionality. It is *only*
provided to help in cases where something in the environment that
cannot be mocked out or controlled influences whether or not an
exception is raised.
|