.. _`warnings`: Warnings Capture ================ .. versionadded:: 3.1 Starting from version ``3.1``, pytest now automatically catches warnings during test execution and displays them at the end of the session:: # content of test_show_warnings.py import warnings def api_v1(): warnings.warn(UserWarning("api v1, should use functions from v2")) return 1 def test_one(): assert api_v1() == 1 Running pytest now produces this output:: $ pytest test_show_warnings.py ======= test session starts ======== platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: collected 1 item test_show_warnings.py . ======= warnings summary ======== test_show_warnings.py::test_one $REGENDOC_TMPDIR/test_show_warnings.py:4: UserWarning: api v1, should use functions from v2 warnings.warn(UserWarning("api v1, should use functions from v2")) -- Docs: http://doc.pytest.org/en/latest/warnings.html ======= 1 passed, 1 warnings in 0.12 seconds ======== Pytest by default catches all warnings except for ``DeprecationWarning`` and ``PendingDeprecationWarning``. The ``-W`` flag can be passed to control which warnings will be displayed or even turn them into errors:: $ pytest -q test_show_warnings.py -W error::UserWarning F ======= FAILURES ======== _______ test_one ________ def test_one(): > assert api_v1() == 1 test_show_warnings.py:8: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ def api_v1(): > warnings.warn(UserWarning("api v1, should use functions from v2")) E UserWarning: api v1, should use functions from v2 test_show_warnings.py:4: UserWarning 1 failed in 0.12 seconds The same option can be set in the ``pytest.ini`` file using the ``filterwarnings`` ini option. For example, the configuration below will ignore all user warnings, but will transform all other warnings into errors. .. code-block:: ini [pytest] filterwarnings = error ignore::UserWarning When a warning matches more than one option in the list, the action for the last matching option is performed. Both ``-W`` command-line option and ``filterwarnings`` ini option are based on Python's own `-W option`_ and `warnings.simplefilter`_, so please refer to those sections in the Python documentation for other examples and advanced usage. ``@pytest.mark.filterwarnings`` ------------------------------- .. versionadded:: 3.2 You can use the ``@pytest.mark.filterwarnings`` to add warning filters to specific test items, allowing you to have finer control of which warnings should be captured at test, class or even module level: .. code-block:: python import warnings def api_v1(): warnings.warn(UserWarning("api v1, should use functions from v2")) return 1 @pytest.mark.filterwarnings('ignore:api v1') def test_one(): assert api_v1() == 1 Filters applied using a mark take precedence over filters passed on the command line or configured by the ``filterwarnings`` ini option. You may apply a filter to all tests of a class by using the ``filterwarnings`` mark as a class decorator or to all tests in a module by setting the ``pytestmark`` variable: .. code-block:: python # turns all warnings into errors for this module pytestmark = @pytest.mark.filterwarnings('error') .. note:: ``DeprecationWarning`` and ``PendingDeprecationWarning`` are hidden by the standard library by default so you have to explicitly configure them to be displayed in your ``pytest.ini``: .. code-block:: ini [pytest] filterwarnings = once::DeprecationWarning once::PendingDeprecationWarning *Credits go to Florian Schulze for the reference implementation in the* `pytest-warnings`_ *plugin.* .. _`-W option`: https://docs.python.org/3/using/cmdline.html?highlight=#cmdoption-W .. _warnings.simplefilter: https://docs.python.org/3/library/warnings.html#warnings.simplefilter .. _`pytest-warnings`: https://github.com/fschulze/pytest-warnings Disabling warning capture ------------------------- This feature is enabled by default but can be disabled entirely in your ``pytest.ini`` file with: .. code-block:: ini [pytest] addopts = -p no:warnings Or passing ``-p no:warnings`` in the command-line. .. _`asserting warnings`: .. _assertwarnings: .. _`asserting warnings with the warns function`: .. _warns: Asserting warnings with the warns function ----------------------------------------------- .. versionadded:: 2.8 You can check that code raises a particular warning using ``pytest.warns``, which works in a similar manner to :ref:`raises `:: import warnings import pytest def test_warning(): with pytest.warns(UserWarning): warnings.warn("my warning", UserWarning) The test will fail if the warning in question is not raised. The keyword argument ``match`` to assert that the exception matches a text or regex:: >>> with warns(UserWarning, match='must be 0 or None'): ... warnings.warn("value must be 0 or None", UserWarning) >>> with warns(UserWarning, match=r'must be \d+$'): ... warnings.warn("value must be 42", UserWarning) >>> with warns(UserWarning, match=r'must be \d+$'): ... warnings.warn("this is not here", UserWarning) Traceback (most recent call last): ... Failed: DID NOT WARN. No warnings of type ...UserWarning... was emitted... You can also call ``pytest.warns`` on a function or code string:: pytest.warns(expected_warning, func, *args, **kwargs) pytest.warns(expected_warning, "func(*args, **kwargs)") The function also returns a list of all raised warnings (as ``warnings.WarningMessage`` objects), which you can query for additional information:: with pytest.warns(RuntimeWarning) as record: warnings.warn("another warning", RuntimeWarning) # check that only one warning was raised assert len(record) == 1 # check that the message matches assert record[0].message.args[0] == "another warning" Alternatively, you can examine raised warnings in detail using the :ref:`recwarn ` fixture (see below). .. note:: ``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated differently; see :ref:`ensuring_function_triggers`. .. _`recording warnings`: .. _recwarn: Recording warnings ------------------------ You can record raised warnings either using ``pytest.warns`` or with the ``recwarn`` fixture. To record with ``pytest.warns`` without asserting anything about the warnings, pass ``None`` as the expected warning type:: with pytest.warns(None) as record: warnings.warn("user", UserWarning) warnings.warn("runtime", RuntimeWarning) assert len(record) == 2 assert str(record[0].message) == "user" assert str(record[1].message) == "runtime" The ``recwarn`` fixture will record warnings for the whole function:: import warnings def test_hello(recwarn): warnings.warn("hello", UserWarning) assert len(recwarn) == 1 w = recwarn.pop(UserWarning) assert issubclass(w.category, UserWarning) assert str(w.message) == "hello" assert w.filename assert w.lineno Both ``recwarn`` and ``pytest.warns`` return the same interface for recorded warnings: a WarningsRecorder instance. To view the recorded warnings, you can iterate over this instance, call ``len`` on it to get the number of recorded warnings, or index into it to get a particular recorded warning. It also provides these methods: .. autoclass:: _pytest.recwarn.WarningsRecorder() :members: Each recorded warning has the attributes ``message``, ``category``, ``filename``, ``lineno``, ``file``, and ``line``. The ``category`` is the class of the warning. The ``message`` is the warning itself; calling ``str(message)`` will return the actual message of the warning. .. note:: :class:`RecordedWarning` was changed from a plain class to a namedtuple in pytest 3.1 .. note:: ``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated differently; see :ref:`ensuring_function_triggers`. .. _`ensuring a function triggers a deprecation warning`: .. _ensuring_function_triggers: Ensuring a function triggers a deprecation warning ------------------------------------------------------- You can also call a global helper for checking that a certain function call triggers a ``DeprecationWarning`` or ``PendingDeprecationWarning``:: import pytest def test_global(): pytest.deprecated_call(myfunction, 17) By default, ``DeprecationWarning`` and ``PendingDeprecationWarning`` will not be caught when using ``pytest.warns`` or ``recwarn`` because default Python warnings filters hide them. If you wish to record them in your own code, use the command ``warnings.simplefilter('always')``:: import warnings import pytest def test_deprecation(recwarn): warnings.simplefilter('always') warnings.warn("deprecated", DeprecationWarning) assert len(recwarn) == 1 assert recwarn.pop(DeprecationWarning) You can also use it as a contextmanager:: def test_global(): with pytest.deprecated_call(): myobject.deprecated_method()