2021-03-15 16:22:11 +08:00
.. _`assert`:
2010-10-14 07:25:09 +08:00
2021-03-11 03:18:21 +08:00
How to write and report assertions in tests
2011-03-04 06:40:38 +08:00
==================================================
2010-10-14 07:25:09 +08:00
2010-11-25 19:11:10 +08:00
.. _`assert with the assert statement`:
2011-09-06 17:43:42 +08:00
Asserting with the `` assert `` statement
2010-10-14 07:25:09 +08:00
---------------------------------------------------------
2021-03-15 16:22:11 +08:00
`` pytest `` allows you to use the standard Python `` assert `` for verifying
2010-10-14 07:25:09 +08:00
expectations and values in Python tests. For example, you can write the
2019-04-12 19:50:26 +08:00
following:
.. code-block :: python
2010-10-14 07:25:09 +08:00
# content of test_assert1.py
def f():
return 3
2019-04-12 19:50:26 +08:00
2010-10-14 07:25:09 +08:00
def test_function():
assert f() == 4
2011-05-28 01:30:27 +08:00
to assert that your function returns a certain value. If this assertion fails
2018-11-24 13:41:22 +08:00
you will see the return value of the function call:
.. code-block :: pytest
2010-10-14 07:25:09 +08:00
2016-06-21 22:16:57 +08:00
$ pytest test_assert1.py
2017-11-23 23:33:41 +08:00
=========================== test session starts ============================
2024-01-02 16:58:20 +08:00
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
2021-10-04 14:56:26 +08:00
rootdir: /home/sweet/project
2017-07-04 07:29:13 +08:00
collected 1 item
2018-05-18 16:19:46 +08:00
2017-11-23 23:33:41 +08:00
test_assert1.py F [100%]
2018-05-18 16:19:46 +08:00
2017-11-23 23:33:41 +08:00
================================= FAILURES =================================
______________________________ test_function _______________________________
2018-05-18 16:19:46 +08:00
2010-10-14 07:25:09 +08:00
def test_function():
> assert f() == 4
E assert 3 == 4
E + where 3 = f()
2018-05-18 16:19:46 +08:00
2019-04-15 22:24:17 +08:00
test_assert1.py:6: AssertionError
2020-03-11 22:23:25 +08:00
========================= short test summary info ==========================
FAILED test_assert1.py::test_function - assert 3 == 4
2019-08-30 23:43:47 +08:00
============================ 1 failed in 0.12s =============================
2010-10-14 07:25:09 +08:00
2014-01-18 19:31:33 +08:00
`` pytest `` has support for showing the values of the most common subexpressions
2011-06-19 04:30:46 +08:00
including calls, attributes, comparisons, and binary and unary
operators. (See :ref: `tbreportdemo` ). This allows you to use the
idiomatic python constructs without boilerplate code while not losing
introspection information.
2011-05-28 01:30:27 +08:00
2023-08-06 03:30:41 +08:00
If a message is specified with the assertion like this:
2019-04-12 19:50:26 +08:00
.. code-block :: python
2011-06-19 04:07:36 +08:00
2011-06-19 04:30:46 +08:00
assert a % 2 == 0, "value was odd, should be even"
2023-08-06 03:30:41 +08:00
it is printed alongside the assertion introspection in the traceback.
2011-05-28 01:30:27 +08:00
2011-06-19 04:30:46 +08:00
See :ref: `assert-details` for more information on assertion introspection.
2011-03-04 06:40:38 +08:00
2012-10-09 20:35:17 +08:00
.. _`assertraises`:
2011-09-06 17:43:42 +08:00
Assertions about expected exceptions
2010-10-14 07:25:09 +08:00
------------------------------------------
In order to write assertions about raised exceptions, you can use
2020-11-19 22:44:59 +08:00
:func: `pytest.raises` as a context manager like this:
2019-04-12 19:50:26 +08:00
.. code-block :: python
2010-10-14 07:25:09 +08:00
2011-03-04 06:40:38 +08:00
import pytest
2014-09-08 21:26:31 +08:00
2019-04-12 19:50:26 +08:00
2014-09-08 21:26:31 +08:00
def test_zero_division():
with pytest.raises(ZeroDivisionError):
1 / 0
2010-10-14 07:25:09 +08:00
2019-04-12 19:50:26 +08:00
and if you need to have access to the actual exception info you may use:
.. code-block :: python
2010-10-14 07:25:09 +08:00
2014-09-08 21:26:31 +08:00
def test_recursion_depth():
with pytest.raises(RuntimeError) as excinfo:
2019-04-12 19:50:26 +08:00
2014-09-08 21:26:31 +08:00
def f():
f()
2019-04-12 19:50:26 +08:00
2010-10-14 07:25:09 +08:00
f()
2019-04-12 19:50:26 +08:00
assert "maximum recursion" in str(excinfo.value)
2010-10-14 07:25:09 +08:00
2021-03-13 17:10:34 +08:00
`` excinfo `` is an :class: `~pytest.ExceptionInfo` instance, which is a wrapper around
2014-09-08 21:26:31 +08:00
the actual exception raised. The main attributes of interest are
`` .type `` , `` .value `` and `` .traceback `` .
2013-08-01 17:12:02 +08:00
2023-11-04 18:24:57 +08:00
Note that `` pytest.raises `` will match the exception type or any subclasses (like the standard `` except `` statement).
If you want to check if a block of code is raising an exact exception type, you need to check that explicitly:
.. code-block :: python
2023-11-04 23:37:47 +08:00
def test_foo_not_implemented():
2023-11-04 18:24:57 +08:00
def foo():
raise NotImplementedError
with pytest.raises(RuntimeError) as excinfo:
foo()
2023-11-04 23:37:47 +08:00
assert excinfo.type is RuntimeError
2023-11-04 18:24:57 +08:00
The :func: `pytest.raises` call will succeed, even though the function raises :class: `NotImplementedError` , because
:class: `NotImplementedError` is a subclass of :class: `RuntimeError` ; however the following `assert` statement will
catch the problem.
Matching exception messages
~~~~~~~~~~~~~~~~~~~~~~~~~~~
2019-02-14 18:32:49 +08:00
You can pass a `` match `` keyword parameter to the context-manager to test
that a regular expression matches on the string representation of an exception
2021-08-18 17:43:59 +08:00
(similar to the `` TestCase.assertRaisesRegex `` method from `` unittest `` ):
2019-04-12 19:50:26 +08:00
.. code-block :: python
2016-06-20 04:56:43 +08:00
2019-02-14 18:32:49 +08:00
import pytest
2016-06-20 04:34:42 +08:00
2019-04-12 19:50:26 +08:00
2019-02-14 18:32:49 +08:00
def myfunc():
raise ValueError("Exception 123 raised")
2016-06-20 04:34:42 +08:00
2019-04-12 19:50:26 +08:00
2019-02-14 18:32:49 +08:00
def test_match():
2019-04-12 19:50:26 +08:00
with pytest.raises(ValueError, match=r".* 123 .* "):
2019-02-14 18:32:49 +08:00
myfunc()
2023-11-04 18:24:57 +08:00
Notes:
* The `` match `` parameter is matched with the :func: `re.search`
function, so in the above example `` match='123' `` would have worked as well.
* The `` match `` parameter also matches against `PEP-678 <https://peps.python.org/pep-0678/> `__ `` __notes__ `` .
2024-01-02 16:58:20 +08:00
.. _`assert-matching-exception-groups`:
2023-11-04 18:24:57 +08:00
Matching exception groups
~~~~~~~~~~~~~~~~~~~~~~~~~
2019-02-14 18:32:49 +08:00
2023-09-18 05:26:58 +08:00
You can also use the :func: `excinfo.group_contains() <pytest.ExceptionInfo.group_contains>`
2024-01-02 16:58:20 +08:00
method to test for exceptions returned as part of an :class: `ExceptionGroup` :
2023-09-18 05:26:58 +08:00
.. code-block :: python
def test_exception_in_group():
2024-01-31 04:00:38 +08:00
with pytest.raises(ExceptionGroup) as excinfo:
2023-09-18 05:26:58 +08:00
raise ExceptionGroup(
"Group message",
[
RuntimeError("Exception 123 raised"),
],
)
assert excinfo.group_contains(RuntimeError, match=r".* 123 .* ")
assert not excinfo.group_contains(TypeError)
The optional `` match `` keyword parameter works the same way as for
:func: `pytest.raises` .
By default `` group_contains() `` will recursively search for a matching
exception at any level of nested `` ExceptionGroup `` instances. You can
specify a `` depth `` keyword parameter if you only want to match an
exception at a specific level; exceptions contained directly in the top
`` ExceptionGroup `` would match `` depth=1 `` .
.. code-block :: python
def test_exception_in_group_at_given_depth():
2024-01-31 04:00:38 +08:00
with pytest.raises(ExceptionGroup) as excinfo:
2023-09-18 05:26:58 +08:00
raise ExceptionGroup(
"Group message",
[
RuntimeError(),
ExceptionGroup(
"Nested group",
[
TypeError(),
],
),
],
)
assert excinfo.group_contains(RuntimeError, depth=1)
assert excinfo.group_contains(TypeError, depth=2)
assert not excinfo.group_contains(RuntimeError, depth=2)
assert not excinfo.group_contains(TypeError, depth=1)
2023-11-04 18:24:57 +08:00
Alternate form (legacy)
~~~~~~~~~~~~~~~~~~~~~~~
There is an alternate form where you pass
a function that will be executed, along `` *args `` and `` **kwargs `` , and :func: `pytest.raises`
will execute the function with the arguments and assert that the given exception is raised:
2019-04-12 19:50:26 +08:00
.. code-block :: python
2010-10-14 07:25:09 +08:00
2023-11-04 18:24:57 +08:00
def func(x):
if x <= 0:
raise ValueError("x needs to be larger than zero")
pytest.raises(ValueError, func, x=-1)
2010-10-14 07:25:09 +08:00
2019-02-14 18:32:49 +08:00
The reporter will provide you with helpful output in case of failures such as *no
2010-10-14 07:25:09 +08:00
exception* or * wrong exception*.
2023-11-04 18:24:57 +08:00
This form was the original :func: `pytest.raises` API, developed before the `` with `` statement was
added to the Python language. Nowadays, this form is rarely used, with the context-manager form (using `` with `` )
being considered more readable.
Nonetheless, this form is fully supported and not deprecated in any way.
xfail mark and pytest.raises
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
It is also possible to specify a `` raises `` argument to
:ref: `pytest.mark.xfail <pytest.mark.xfail ref>` , which checks that the test is failing in a more
2019-04-12 19:50:26 +08:00
specific way than just having any exception raised:
.. code-block :: python
2014-07-27 00:10:32 +08:00
2023-11-04 18:24:57 +08:00
def f():
raise IndexError()
2014-07-27 00:10:32 +08:00
@pytest.mark.xfail(raises=IndexError)
def test_f():
f()
2023-11-04 18:24:57 +08:00
This will only "xfail" if the test fails by raising `` IndexError `` or subclasses.
* Using :ref: `pytest.mark.xfail <pytest.mark.xfail ref>` with the `` raises `` parameter is probably better for something
like documenting unfixed bugs (where the test describes what "should" happen) or bugs in dependencies.
* Using :func: `pytest.raises` is likely to be better for cases where you are
testing exceptions your own code is deliberately raising, which is the majority of cases.
2014-07-27 00:10:32 +08:00
2015-07-29 07:01:11 +08:00
.. _`assertwarns`:
Assertions about expected warnings
-----------------------------------------
2019-04-28 23:37:58 +08:00
2015-07-29 07:01:11 +08:00
You can check that code raises a particular warning using
:ref: `pytest.warns <warns>` .
2014-07-27 00:10:32 +08:00
2010-11-26 03:06:42 +08:00
.. _newreport:
2010-10-14 07:25:09 +08:00
Making use of context-sensitive comparisons
-------------------------------------------------
2019-04-28 23:37:58 +08:00
2010-10-14 07:25:09 +08:00
2014-01-18 19:31:33 +08:00
`` pytest `` has rich support for providing context-sensitive information
2019-04-12 19:50:26 +08:00
when it encounters comparisons. For example:
.. code-block :: python
2010-10-14 07:25:09 +08:00
# content of test_assert2.py
def test_set_comparison():
set1 = set("1308")
set2 = set("8035")
assert set1 == set2
2018-11-24 13:41:22 +08:00
if you run this module:
.. code-block :: pytest
2010-10-14 07:25:09 +08:00
2016-06-21 22:16:57 +08:00
$ pytest test_assert2.py
2017-11-23 23:33:41 +08:00
=========================== test session starts ============================
2024-01-02 16:58:20 +08:00
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
2021-10-04 14:56:26 +08:00
rootdir: /home/sweet/project
2017-07-04 07:29:13 +08:00
collected 1 item
2018-05-18 16:19:46 +08:00
2017-11-23 23:33:41 +08:00
test_assert2.py F [100%]
2018-05-18 16:19:46 +08:00
2017-11-23 23:33:41 +08:00
================================= FAILURES =================================
___________________________ test_set_comparison ____________________________
2018-05-18 16:19:46 +08:00
2010-10-14 07:25:09 +08:00
def test_set_comparison():
set1 = set("1308")
set2 = set("8035")
> assert set1 == set2
2017-03-14 06:41:20 +08:00
E AssertionError: assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
2024-01-02 16:58:20 +08:00
E
2010-10-14 07:25:09 +08:00
E Extra items in the left set:
E '1'
E Extra items in the right set:
E '5'
2022-03-13 22:58:05 +08:00
E Use -v to get more diff
2018-05-18 16:19:46 +08:00
2021-10-04 14:56:26 +08:00
test_assert2.py:4: AssertionError
2020-03-11 22:23:25 +08:00
========================= short test summary info ==========================
FAILED test_assert2.py::test_set_comparison - AssertionError: assert {'0'...
2019-08-30 23:43:47 +08:00
============================ 1 failed in 0.12s =============================
2010-10-14 07:25:09 +08:00
Special comparisons are done for a number of cases:
* comparing long strings: a context diff is shown
* comparing long sequences: first failing indices
* comparing dicts: different entries
2010-11-26 03:06:42 +08:00
See the :ref: `reporting demo <tbreportdemo>` for many more examples.
2010-11-25 20:00:01 +08:00
2019-02-10 21:34:35 +08:00
Defining your own explanation for failed assertions
---------------------------------------------------
2011-06-21 00:12:48 +08:00
It is possible to add your own detailed explanations by implementing
the `` pytest_assertrepr_compare `` hook.
.. autofunction :: _pytest.hookspec.pytest_assertrepr_compare
2016-08-23 10:35:41 +08:00
:noindex:
2011-06-21 00:12:48 +08:00
2018-05-18 16:19:46 +08:00
As an example consider adding the following hook in a :ref: `conftest.py <conftest.py>`
2019-04-12 19:50:26 +08:00
file which provides an alternative explanation for `` Foo `` objects:
.. code-block :: python
2011-06-21 00:12:48 +08:00
# content of conftest.py
from test_foocompare import Foo
2019-04-12 19:50:26 +08:00
2011-06-21 00:12:48 +08:00
def pytest_assertrepr_compare(op, left, right):
if isinstance(left, Foo) and isinstance(right, Foo) and op == "==":
2019-08-07 03:40:27 +08:00
return [
"Comparing Foo instances:",
2022-04-28 22:30:16 +08:00
f" vals: {left.val} != {right.val}",
2019-08-07 03:40:27 +08:00
]
2011-06-21 00:12:48 +08:00
2019-04-12 19:50:26 +08:00
now, given this test module:
.. code-block :: python
2011-06-21 00:12:48 +08:00
# content of test_foocompare.py
2019-08-07 03:40:27 +08:00
class Foo:
2011-06-21 00:12:48 +08:00
def __init__(self, val):
2015-09-21 21:23:26 +08:00
self.val = val
def __eq__(self, other):
return self.val == other.val
2011-06-21 00:12:48 +08:00
2019-04-12 19:50:26 +08:00
2011-06-21 00:12:48 +08:00
def test_compare():
f1 = Foo(1)
f2 = Foo(2)
assert f1 == f2
2014-01-18 19:31:33 +08:00
you can run the test module and get the custom output defined in
2018-11-24 13:41:22 +08:00
the conftest file:
.. code-block :: pytest
2011-06-21 00:12:48 +08:00
2016-06-21 22:16:57 +08:00
$ pytest -q test_foocompare.py
2017-11-23 23:33:41 +08:00
F [100%]
================================= FAILURES =================================
_______________________________ test_compare _______________________________
2018-05-18 16:19:46 +08:00
2011-06-21 00:12:48 +08:00
def test_compare():
f1 = Foo(1)
f2 = Foo(2)
> assert f1 == f2
2016-08-04 04:31:44 +08:00
E assert Comparing Foo instances:
E vals: 1 != 2
2018-05-18 16:19:46 +08:00
2019-04-15 22:24:17 +08:00
test_foocompare.py:12: AssertionError
2020-03-11 22:23:25 +08:00
========================= short test summary info ==========================
FAILED test_foocompare.py::test_compare - assert Comparing Foo instances:
2019-09-18 21:11:59 +08:00
1 failed in 0.12s
2011-05-27 10:08:55 +08:00
2011-05-28 01:30:27 +08:00
.. _assert-details:
2011-06-15 13:50:34 +08:00
.. _`assert introspection`:
2011-05-28 01:30:27 +08:00
2019-02-27 07:39:06 +08:00
Assertion introspection details
-------------------------------
2011-06-15 13:50:34 +08:00
2019-04-28 23:37:58 +08:00
2017-01-13 23:52:02 +08:00
Reporting details about a failing assertion is achieved by rewriting assert
statements before they are run. Rewritten assert statements put introspection
information into the assertion failure message. `` pytest `` only rewrites test
2018-09-25 13:49:50 +08:00
modules directly discovered by its test collection process, so **asserts in
supporting modules which are not themselves test modules will not be rewritten**.
2018-09-25 19:12:55 +08:00
You can manually enable assertion rewriting for an imported module by calling
2021-09-27 06:42:37 +08:00
:ref: `register_assert_rewrite <assertion-rewriting>`
2019-02-27 07:39:06 +08:00
before you import it (a good place to do that is in your root `` conftest.py `` ).
2011-05-27 10:08:55 +08:00
2019-02-27 07:39:06 +08:00
For further information, Benjamin Peterson wrote up `Behind the scenes of pytest's new assertion rewriting <http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html> `_ .
2011-05-29 08:00:47 +08:00
2019-02-27 07:39:06 +08:00
Assertion rewriting caches files on disk
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2017-04-19 19:19:19 +08:00
2019-02-27 07:39:06 +08:00
`` pytest `` will write back the rewritten modules to disk for caching. You can disable
2019-02-27 18:46:03 +08:00
this behavior (for example to avoid leaving stale `` .pyc `` files around in projects that
2019-02-27 07:39:06 +08:00
move files around a lot) by adding this to the top of your `` conftest.py `` file:
2017-04-19 19:19:19 +08:00
2019-02-27 07:39:06 +08:00
.. code-block :: python
2017-04-19 19:19:19 +08:00
2019-02-27 07:39:06 +08:00
import sys
2017-04-19 19:19:19 +08:00
2019-02-27 07:39:06 +08:00
sys.dont_write_bytecode = True
2017-04-19 19:19:19 +08:00
2019-02-27 18:46:03 +08:00
Note that you still get the benefits of assertion introspection, the only change is that
2019-02-27 07:39:06 +08:00
the `` .pyc `` files won't be cached on disk.
Additionally, rewriting will silently skip caching if it cannot write new `` .pyc `` files,
i.e. in a read-only filesystem or a zipfile.
Disabling assert rewriting
~~~~~~~~~~~~~~~~~~~~~~~~~~
`` pytest `` rewrites test modules on import by using an import
hook to write new `` pyc `` files. Most of the time this works transparently.
2019-02-27 18:46:03 +08:00
However, if you are working with the import machinery yourself, the import hook may
2019-02-27 07:39:06 +08:00
interfere.
If this is the case you have two options:
* Disable rewriting for a specific module by adding the string
`` PYTEST_DONT_REWRITE `` to its docstring.
* Disable rewriting for all modules by using `` --assert=plain `` .