1.. _`warnings`:
2
3Warnings Capture
4================
5
6.. versionadded:: 3.1
7
8Starting from version ``3.1``, pytest now automatically catches warnings during test execution
9and displays them at the end of the session::
10
11    # content of test_show_warnings.py
12    import warnings
13
14    def api_v1():
15        warnings.warn(UserWarning("api v1, should use functions from v2"))
16        return 1
17
18    def test_one():
19        assert api_v1() == 1
20
21Running pytest now produces this output::
22
23    $ pytest test_show_warnings.py
24    =========================== test session starts ============================
25    platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
26    rootdir: $REGENDOC_TMPDIR, inifile:
27    collected 1 item
28
29    test_show_warnings.py .                                              [100%]
30
31    ============================= warnings summary =============================
32    test_show_warnings.py::test_one
33      $REGENDOC_TMPDIR/test_show_warnings.py:4: UserWarning: api v1, should use functions from v2
34        warnings.warn(UserWarning("api v1, should use functions from v2"))
35
36    -- Docs: http://doc.pytest.org/en/latest/warnings.html
37    =================== 1 passed, 1 warnings in 0.12 seconds ===================
38
39Pytest by default catches all warnings except for ``DeprecationWarning`` and ``PendingDeprecationWarning``.
40
41The ``-W`` flag can be passed to control which warnings will be displayed or even turn
42them into errors::
43
44    $ pytest -q test_show_warnings.py -W error::UserWarning
45    F                                                                    [100%]
46    ================================= FAILURES =================================
47    _________________________________ test_one _________________________________
48
49        def test_one():
50    >       assert api_v1() == 1
51
52    test_show_warnings.py:8:
53    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
54
55        def api_v1():
56    >       warnings.warn(UserWarning("api v1, should use functions from v2"))
57    E       UserWarning: api v1, should use functions from v2
58
59    test_show_warnings.py:4: UserWarning
60    1 failed in 0.12 seconds
61
62The same option can be set in the ``pytest.ini`` file using the ``filterwarnings`` ini option.
63For example, the configuration below will ignore all user warnings, but will transform
64all other warnings into errors.
65
66.. code-block:: ini
67
68    [pytest]
69    filterwarnings =
70        error
71        ignore::UserWarning
72
73
74When a warning matches more than one option in the list, the action for the last matching option
75is performed.
76
77Both ``-W`` command-line option and ``filterwarnings`` ini option are based on Python's own
78`-W option`_ and `warnings.simplefilter`_, so please refer to those sections in the Python
79documentation for other examples and advanced usage.
80
81
82.. _`filterwarnings`:
83
84``@pytest.mark.filterwarnings``
85-------------------------------
86
87.. versionadded:: 3.2
88
89You can use the ``@pytest.mark.filterwarnings`` to add warning filters to specific test items,
90allowing you to have finer control of which warnings should be captured at test, class or
91even module level:
92
93.. code-block:: python
94
95    import warnings
96
97
98    def api_v1():
99        warnings.warn(UserWarning("api v1, should use functions from v2"))
100        return 1
101
102
103    @pytest.mark.filterwarnings("ignore:api v1")
104    def test_one():
105        assert api_v1() == 1
106
107
108Filters applied using a mark take precedence over filters passed on the command line or configured
109by the ``filterwarnings`` ini option.
110
111You may apply a filter to all tests of a class by using the ``filterwarnings`` mark as a class
112decorator or to all tests in a module by setting the ``pytestmark`` variable:
113
114.. code-block:: python
115
116    # turns all warnings into errors for this module
117    pytestmark = pytest.mark.filterwarnings("error")
118
119
120.. note::
121
122    Except for these features, pytest does not change the python warning filter; it only captures
123    and displays the warnings which are issued with respect to the currently configured filter,
124    including changes to the filter made by test functions or by the system under test.
125
126.. note::
127
128    ``DeprecationWarning`` and ``PendingDeprecationWarning`` are hidden by the standard library
129    by default so you have to explicitly configure them to be displayed in your ``pytest.ini``:
130
131    .. code-block:: ini
132
133        [pytest]
134        filterwarnings =
135            once::DeprecationWarning
136            once::PendingDeprecationWarning
137
138
139*Credits go to Florian Schulze for the reference implementation in the* `pytest-warnings`_
140*plugin.*
141
142.. _`-W option`: https://docs.python.org/3/using/cmdline.html?highlight=#cmdoption-W
143.. _warnings.simplefilter: https://docs.python.org/3/library/warnings.html#warnings.simplefilter
144.. _`pytest-warnings`: https://github.com/fschulze/pytest-warnings
145
146
147Disabling warning capture
148-------------------------
149
150This feature is enabled by default but can be disabled entirely in your ``pytest.ini`` file with:
151
152    .. code-block:: ini
153
154        [pytest]
155        addopts = -p no:warnings
156
157Or passing ``-p no:warnings`` in the command-line.
158
159.. _`asserting warnings`:
160
161.. _assertwarnings:
162
163.. _`asserting warnings with the warns function`:
164
165.. _warns:
166
167Asserting warnings with the warns function
168-----------------------------------------------
169
170.. versionadded:: 2.8
171
172You can check that code raises a particular warning using ``pytest.warns``,
173which works in a similar manner to :ref:`raises <assertraises>`::
174
175    import warnings
176    import pytest
177
178    def test_warning():
179        with pytest.warns(UserWarning):
180            warnings.warn("my warning", UserWarning)
181
182The test will fail if the warning in question is not raised. The keyword
183argument ``match`` to assert that the exception matches a text or regex::
184
185    >>> with warns(UserWarning, match='must be 0 or None'):
186    ...     warnings.warn("value must be 0 or None", UserWarning)
187
188    >>> with warns(UserWarning, match=r'must be \d+$'):
189    ...     warnings.warn("value must be 42", UserWarning)
190
191    >>> with warns(UserWarning, match=r'must be \d+$'):
192    ...     warnings.warn("this is not here", UserWarning)
193    Traceback (most recent call last):
194      ...
195    Failed: DID NOT WARN. No warnings of type ...UserWarning... was emitted...
196
197You can also call ``pytest.warns`` on a function or code string::
198
199    pytest.warns(expected_warning, func, *args, **kwargs)
200    pytest.warns(expected_warning, "func(*args, **kwargs)")
201
202The function also returns a list of all raised warnings (as
203``warnings.WarningMessage`` objects), which you can query for
204additional information::
205
206    with pytest.warns(RuntimeWarning) as record:
207        warnings.warn("another warning", RuntimeWarning)
208
209    # check that only one warning was raised
210    assert len(record) == 1
211    # check that the message matches
212    assert record[0].message.args[0] == "another warning"
213
214Alternatively, you can examine raised warnings in detail using the
215:ref:`recwarn <recwarn>` fixture (see below).
216
217.. note::
218    ``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated
219    differently; see :ref:`ensuring_function_triggers`.
220
221.. _`recording warnings`:
222
223.. _recwarn:
224
225Recording warnings
226------------------------
227
228You can record raised warnings either using ``pytest.warns`` or with
229the ``recwarn`` fixture.
230
231To record with ``pytest.warns`` without asserting anything about the warnings,
232pass ``None`` as the expected warning type::
233
234    with pytest.warns(None) as record:
235        warnings.warn("user", UserWarning)
236        warnings.warn("runtime", RuntimeWarning)
237
238    assert len(record) == 2
239    assert str(record[0].message) == "user"
240    assert str(record[1].message) == "runtime"
241
242The ``recwarn`` fixture will record warnings for the whole function::
243
244    import warnings
245
246    def test_hello(recwarn):
247        warnings.warn("hello", UserWarning)
248        assert len(recwarn) == 1
249        w = recwarn.pop(UserWarning)
250        assert issubclass(w.category, UserWarning)
251        assert str(w.message) == "hello"
252        assert w.filename
253        assert w.lineno
254
255Both ``recwarn`` and ``pytest.warns`` return the same interface for recorded
256warnings: a WarningsRecorder instance. To view the recorded warnings, you can
257iterate over this instance, call ``len`` on it to get the number of recorded
258warnings, or index into it to get a particular recorded warning.
259
260.. currentmodule:: _pytest.warnings
261
262Full API: :class:`WarningsRecorder`.
263
264.. _`ensuring a function triggers a deprecation warning`:
265
266.. _ensuring_function_triggers:
267
268Ensuring a function triggers a deprecation warning
269-------------------------------------------------------
270
271You can also call a global helper for checking
272that a certain function call triggers a ``DeprecationWarning`` or
273``PendingDeprecationWarning``::
274
275    import pytest
276
277    def test_global():
278        pytest.deprecated_call(myfunction, 17)
279
280By default, ``DeprecationWarning`` and ``PendingDeprecationWarning`` will not be
281caught when using ``pytest.warns`` or ``recwarn`` because default Python warnings filters hide
282them. If you wish to record them in your own code, use the
283command ``warnings.simplefilter('always')``::
284
285    import warnings
286    import pytest
287
288    def test_deprecation(recwarn):
289        warnings.simplefilter('always')
290        warnings.warn("deprecated", DeprecationWarning)
291        assert len(recwarn) == 1
292        assert recwarn.pop(DeprecationWarning)
293
294You can also use it as a contextmanager::
295
296    def test_global():
297        with pytest.deprecated_call():
298            myobject.deprecated_method()
299