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