380 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
			
		
		
	
	
			380 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
| .. _`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:
 | |
| 
 | |
| .. code-block:: pytest
 | |
| 
 | |
|     $ pytest test_show_warnings.py
 | |
|     =========================== test session starts ============================
 | |
|     platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
 | |
|     rootdir: $REGENDOC_TMPDIR, inifile:
 | |
|     collected 1 item
 | |
| 
 | |
|     test_show_warnings.py .                                              [100%]
 | |
| 
 | |
|     ============================= 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: https://docs.pytest.org/en/latest/warnings.html
 | |
|     =================== 1 passed, 1 warnings in 0.12 seconds ===================
 | |
| 
 | |
| The ``-W`` flag can be passed to control which warnings will be displayed or even turn
 | |
| them into errors:
 | |
| 
 | |
| .. code-block:: pytest
 | |
| 
 | |
|     $ pytest -q test_show_warnings.py -W error::UserWarning
 | |
|     F                                                                    [100%]
 | |
|     ================================= 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.
 | |
| 
 | |
| .. _`filterwarnings`:
 | |
| 
 | |
| ``@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")
 | |
| 
 | |
| 
 | |
| 
 | |
| *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 warnings summary
 | |
| --------------------------
 | |
| 
 | |
| Although not recommended, you can use the ``--disable-warnings`` command-line option to suppress the
 | |
| warning summary entirely from the test run output.
 | |
| 
 | |
| Disabling warning capture entirely
 | |
| ----------------------------------
 | |
| 
 | |
| This plugin 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. This might be useful if your test suites handles warnings
 | |
| using an external system.
 | |
| 
 | |
| 
 | |
| .. _`deprecation-warnings`:
 | |
| 
 | |
| DeprecationWarning and PendingDeprecationWarning
 | |
| ------------------------------------------------
 | |
| 
 | |
| .. versionadded:: 3.8
 | |
| .. versionchanged:: 3.9
 | |
| 
 | |
| By default pytest will display ``DeprecationWarning`` and ``PendingDeprecationWarning`` warnings from
 | |
| user code and third-party libraries, as recommended by `PEP-0565 <https://www.python.org/dev/peps/pep-0565>`_.
 | |
| This helps users keep their code modern and avoid breakages when deprecated warnings are effectively removed.
 | |
| 
 | |
| Sometimes it is useful to hide some specific deprecation warnings that happen in code that you have no control over
 | |
| (such as third-party libraries), in which case you might use the warning filters options (ini or marks) to ignore
 | |
| those warnings.
 | |
| 
 | |
| For example:
 | |
| 
 | |
| .. code-block:: ini
 | |
| 
 | |
|     [pytest]
 | |
|     filterwarnings =
 | |
|         ignore:.*U.*mode is deprecated:DeprecationWarning
 | |
| 
 | |
| 
 | |
| This will ignore all warnings of type ``DeprecationWarning`` where the start of the message matches
 | |
| the regular expression ``".*U.*mode is deprecated"``.
 | |
| 
 | |
| .. note::
 | |
|     If warnings are configured at the interpreter level, using
 | |
|     the `PYTHONWARNINGS <https://docs.python.org/3/using/cmdline.html#envvar-PYTHONWARNINGS>`_ environment variable or the
 | |
|     ``-W`` command-line option, pytest will not configure any filters by default.
 | |
| 
 | |
|     Also pytest doesn't follow ``PEP-0506`` suggestion of resetting all warning filters because
 | |
|     it might break test suites that configure warning filters themselves
 | |
|     by calling ``warnings.simplefilter`` (see issue `#2430 <https://github.com/pytest-dev/pytest/issues/2430>`_
 | |
|     for an example of that).
 | |
| 
 | |
| 
 | |
| .. _`ensuring a function triggers a deprecation warning`:
 | |
| 
 | |
| .. _ensuring_function_triggers:
 | |
| 
 | |
| Ensuring code 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()
 | |
| 
 | |
| 
 | |
| 
 | |
| .. _`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 <assertraises>`::
 | |
| 
 | |
|     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 <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.
 | |
| 
 | |
| .. currentmodule:: _pytest.warnings
 | |
| 
 | |
| Full API: :class:`WarningsRecorder`.
 | |
| 
 | |
| 
 | |
| .. _internal-warnings:
 | |
| 
 | |
| Internal pytest warnings
 | |
| ------------------------
 | |
| 
 | |
| .. versionadded:: 3.8
 | |
| 
 | |
| pytest may generate its own warnings in some situations, such as improper usage or deprecated features.
 | |
| 
 | |
| For example, pytest will emit a warning if it encounters a class that matches :confval:`python_classes` but also
 | |
| defines an ``__init__`` constructor, as this prevents the class from being instantiated:
 | |
| 
 | |
| .. code-block:: python
 | |
| 
 | |
|     # content of test_pytest_warnings.py
 | |
|     class Test:
 | |
|         def __init__(self):
 | |
|             pass
 | |
| 
 | |
|         def test_foo(self):
 | |
|             assert 1 == 1
 | |
| 
 | |
| .. code-block:: pytest
 | |
| 
 | |
|     $ pytest test_pytest_warnings.py -q
 | |
| 
 | |
|     ============================= warnings summary =============================
 | |
|     test_pytest_warnings.py:1
 | |
|       $REGENDOC_TMPDIR/test_pytest_warnings.py:1: PytestWarning: cannot collect test class 'Test' because it has a __init__ constructor
 | |
|         class Test:
 | |
| 
 | |
|     -- Docs: https://docs.pytest.org/en/latest/warnings.html
 | |
|     1 warnings in 0.12 seconds
 | |
| 
 | |
| These warnings might be filtered using the same builtin mechanisms used to filter other types of warnings.
 | |
| 
 | |
| Please read our :ref:`backwards-compatibility` to learn how we proceed about deprecating and eventually removing
 | |
| features.
 | |
| 
 | |
| The following warning types ares used by pytest and are part of the public API:
 | |
| 
 | |
| .. autoclass:: pytest.PytestWarning
 | |
| 
 | |
| .. autoclass:: pytest.PytestDeprecationWarning
 | |
| 
 | |
| .. autoclass:: pytest.RemovedInPytest4Warning
 | |
| 
 | |
| .. autoclass:: pytest.PytestExperimentalApiWarning
 |