2017-03-21 10:35:01 +08:00
.. _`warnings`:
2021-03-15 16:22:11 +08:00
How to capture warnings
=======================
2017-03-21 10:35:01 +08:00
2019-04-28 23:37:58 +08:00
2017-03-21 10:35:01 +08:00
2017-05-30 05:59:34 +08:00
Starting from version `` 3.1 `` , pytest now automatically catches warnings during test execution
2019-04-12 19:50:26 +08:00
and displays them at the end of the session:
.. code-block :: python
2017-03-21 10:35:01 +08:00
# content of test_show_warnings.py
import warnings
2019-04-12 19:50:26 +08:00
2017-05-30 05:59:34 +08:00
def api_v1():
warnings.warn(UserWarning("api v1, should use functions from v2"))
2017-03-21 10:35:01 +08:00
return 1
2019-04-12 19:50:26 +08:00
2017-03-21 10:35:01 +08:00
def test_one():
2017-05-30 05:59:34 +08:00
assert api_v1() == 1
2017-03-21 10:35:01 +08:00
2018-11-24 13:41:22 +08:00
Running pytest now produces this output:
.. code-block :: pytest
2017-03-21 10:35:01 +08:00
$ pytest test_show_warnings.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_show_warnings.py . [100%]
2018-05-18 16:19:46 +08:00
2017-11-23 23:33:41 +08:00
============================= warnings summary =============================
2018-10-27 21:07:54 +08:00
test_show_warnings.py::test_one
2021-10-04 14:56:26 +08:00
/home/sweet/project/test_show_warnings.py:5: UserWarning: api v1, should use functions from v2
2018-10-27 21:07:54 +08:00
warnings.warn(UserWarning("api v1, should use functions from v2"))
2018-05-18 16:19:46 +08:00
2021-07-06 15:09:04 +08:00
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
2019-11-19 23:43:51 +08:00
======================= 1 passed, 1 warning in 0.12s =======================
2017-03-21 10:35:01 +08:00
2022-08-17 22:44:58 +08:00
.. _`controlling-warnings`:
2022-01-04 21:55:01 +08:00
Controlling warnings
--------------------
Similar to Python's `warning filter`_ and :option: `-W option <python:-W>` flag, pytest provides
its own `` -W `` flag to control which warnings are ignored, displayed, or turned into
errors. See the `warning filter`_ documentation for more
advanced use-cases.
.. _`warning filter`: https://docs.python.org/3/library/warnings.html#warning-filter
This code sample shows how to treat any `` UserWarning `` category class of warning
as an error:
2018-11-24 13:41:22 +08:00
.. code-block :: pytest
2017-03-21 10:35:01 +08:00
2017-05-30 05:59:34 +08:00
$ pytest -q test_show_warnings.py -W error::UserWarning
2017-11-23 23:33:41 +08:00
F [100%]
================================= FAILURES =================================
_________________________________ test_one _________________________________
2018-05-18 16:19:46 +08:00
2017-05-20 06:12:59 +08:00
def test_one():
2017-05-31 05:19:34 +08:00
> assert api_v1() == 1
2018-05-18 16:19:46 +08:00
2019-04-15 22:24:17 +08:00
test_show_warnings.py:10:
2018-05-18 16:19:46 +08:00
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
2017-05-31 05:19:34 +08:00
def api_v1():
> warnings.warn(UserWarning("api v1, should use functions from v2"))
E UserWarning: api v1, should use functions from v2
2018-05-18 16:19:46 +08:00
2019-04-15 22:24:17 +08:00
test_show_warnings.py:5: UserWarning
2020-03-11 22:23:25 +08:00
========================= short test summary info ==========================
FAILED test_show_warnings.py::test_one - UserWarning: api v1, should use ...
2019-09-18 21:11:59 +08:00
1 failed in 0.12s
2017-03-21 10:35:01 +08:00
2020-08-14 15:08:17 +08:00
The same option can be set in the `` pytest.ini `` or `` pyproject.toml `` file using the
`` filterwarnings `` ini option. For example, the configuration below will ignore all
user warnings and specific deprecation warnings matching a regex, but will transform
2017-03-21 10:35:01 +08:00
all other warnings into errors.
.. code-block :: ini
2020-08-14 15:08:17 +08:00
# pytest.ini
2017-03-21 10:35:01 +08:00
[pytest]
filterwarnings =
error
2017-05-30 05:59:34 +08:00
ignore::UserWarning
2020-08-14 15:08:17 +08:00
ignore:function ham\(\) is deprecated:DeprecationWarning
.. code-block :: toml
# pyproject.toml
[tool.pytest.ini_options]
filterwarnings = [
"error",
"ignore::UserWarning",
# note the use of single quote below to denote "raw" strings in TOML
'ignore:function ham\(\) is deprecated:DeprecationWarning',
]
2017-03-21 10:35:01 +08:00
When a warning matches more than one option in the list, the action for the last matching option
is performed.
2023-02-08 06:27:34 +08:00
.. note ::
The `` -W `` flag and the `` filterwarnings `` ini option use warning filters that are
similar in structure, but each configuration option interprets its filter
differently. For example, *message* in `` filterwarnings `` is a string containing a
regular expression that the start of the warning message must match,
case-insensitively, while *message* in `` -W `` is a literal string that the start of
the warning message must contain (case-insensitively), ignoring any whitespace at
the start or end of message. Consult the `warning filter`_ documentation for more
details.
2018-10-17 08:00:57 +08:00
.. _`filterwarnings`:
`` @pytest.mark.filterwarnings ``
-------------------------------
2019-04-28 23:37:58 +08:00
2018-10-17 08:00:57 +08:00
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
2020-07-10 20:50:03 +08:00
decorator or to all tests in a module by setting the :globalvar: `pytestmark` variable:
2018-10-17 08:00:57 +08:00
.. 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.*
.. _`pytest-warnings`: https://github.com/fschulze/pytest-warnings
Disabling warnings summary
--------------------------
2018-09-04 04:52:44 +08:00
Although not recommended, you can use the `` --disable-warnings `` command-line option to suppress the
warning summary entirely from the test run output.
2018-09-04 20:30:13 +08:00
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`:
2018-09-04 04:52:44 +08:00
DeprecationWarning and PendingDeprecationWarning
------------------------------------------------
2018-10-17 08:00:57 +08:00
By default pytest will display `` DeprecationWarning `` and `` PendingDeprecationWarning `` warnings from
2021-09-27 06:42:37 +08:00
user code and third-party libraries, as recommended by :pep: `565` .
2018-10-17 08:00:57 +08:00
This helps users keep their code modern and avoid breakages when deprecated warnings are effectively removed.
2018-09-04 04:52:44 +08:00
2022-08-17 22:44:58 +08:00
However, in the specific case where users capture any type of warnings in their test, either with
:func: `pytest.warns` , :func: `pytest.deprecated_call` or using the :ref: `recwarn <recwarn>` fixture,
no warning will be displayed at all.
2018-09-22 19:27:53 +08:00
Sometimes it is useful to hide some specific deprecation warnings that happen in code that you have no control over
2018-10-17 08:00:57 +08:00
(such as third-party libraries), in which case you might use the warning filters options (ini or marks) to ignore
those warnings.
2018-09-22 19:27:53 +08:00
For example:
2018-09-04 04:52:44 +08:00
.. code-block :: ini
[pytest]
filterwarnings =
2018-09-22 19:27:53 +08:00
ignore:.*U.* mode is deprecated:DeprecationWarning
2018-10-17 08:00:57 +08:00
This will ignore all warnings of type `` DeprecationWarning `` where the start of the message matches
the regular expression `` ".*U.*mode is deprecated" `` .
2022-08-17 22:44:58 +08:00
See :ref: `@pytest.mark.filterwarnings <filterwarnings>` and
:ref: `Controlling warnings <controlling-warnings>` for more examples.
2018-09-22 19:27:53 +08:00
.. note ::
2019-08-07 04:25:54 +08:00
2018-09-22 19:27:53 +08:00
If warnings are configured at the interpreter level, using
2021-08-26 22:05:03 +08:00
the :envvar: `python:PYTHONWARNINGS` environment variable or the
2018-09-22 19:27:53 +08:00
`` -W `` command-line option, pytest will not configure any filters by default.
2018-09-04 04:52:44 +08:00
2021-09-27 06:42:37 +08:00
Also pytest doesn't follow :pep: `506` suggestion of resetting all warning filters because
2018-10-17 08:00:57 +08:00
it might break test suites that configure warning filters themselves
2021-11-06 17:16:11 +08:00
by calling :func: `warnings.simplefilter` (see :issue: `2430` for an example of that).
2018-09-04 20:30:13 +08:00
2018-03-23 00:02:30 +08:00
2018-10-17 08:00:57 +08:00
.. _`ensuring a function triggers a deprecation warning`:
2017-07-21 09:02:21 +08:00
2018-10-17 08:00:57 +08:00
.. _ensuring_function_triggers:
2017-07-21 09:02:21 +08:00
2018-10-17 08:00:57 +08:00
Ensuring code triggers a deprecation warning
--------------------------------------------
2017-07-21 09:02:21 +08:00
2019-12-23 18:38:27 +08:00
You can also use :func: `pytest.deprecated_call` for checking
2018-10-17 08:00:57 +08:00
that a certain function call triggers a `` DeprecationWarning `` or
2019-04-12 19:50:26 +08:00
`` PendingDeprecationWarning `` :
.. code-block :: python
2017-07-21 09:02:21 +08:00
2018-10-17 08:00:57 +08:00
import pytest
2017-07-21 09:02:21 +08:00
2019-04-12 19:50:26 +08:00
2019-12-23 18:38:27 +08:00
def test_myfunction_deprecated():
with pytest.deprecated_call():
myfunction(17)
This test will fail if `` myfunction `` does not issue a deprecation warning
when called with a `` 17 `` argument.
2018-01-22 04:43:00 +08:00
2019-04-12 19:50:26 +08:00
2017-05-30 05:59:34 +08:00
2017-03-21 10:35:01 +08:00
.. _`asserting warnings`:
.. _assertwarnings:
.. _`asserting warnings with the warns function`:
.. _warns:
Asserting warnings with the warns function
2019-02-02 10:48:29 +08:00
------------------------------------------
2017-03-21 10:35:01 +08:00
2021-06-24 03:34:48 +08:00
You can check that code raises a particular warning using :func: `pytest.warns` ,
2022-08-17 22:44:58 +08:00
which works in a similar manner to :ref: `raises <assertraises>` (except that
:ref: `raises <assertraises>` does not capture all exceptions, only the
`` expected_exception `` ):
2019-04-12 19:50:26 +08:00
.. code-block :: python
2017-03-21 10:35:01 +08:00
import warnings
2022-04-28 22:30:16 +08:00
2017-03-21 10:35:01 +08:00
import pytest
2019-04-12 19:50:26 +08:00
2017-03-21 10:35:01 +08:00
def test_warning():
with pytest.warns(UserWarning):
warnings.warn("my warning", UserWarning)
2022-08-17 22:44:58 +08:00
The test will fail if the warning in question is not raised. Use the keyword
2023-01-27 19:11:00 +08:00
argument `` match `` to assert that the warning matches a text or regex.
To match a literal string that may contain regular expression metacharacters like `` ( `` or `` . `` , the pattern can
first be escaped with `` re.escape `` .
2017-09-07 16:28:52 +08:00
2023-01-27 19:11:00 +08:00
Some examples:
.. code-block :: pycon
>>> with warns(UserWarning, match="must be 0 or None"):
2017-09-07 16:28:52 +08:00
... warnings.warn("value must be 0 or None", UserWarning)
2023-01-27 19:11:00 +08:00
...
2017-09-07 16:28:52 +08:00
2023-01-27 19:11:00 +08:00
>>> with warns(UserWarning, match=r"must be \d+$"):
2017-09-07 16:28:52 +08:00
... warnings.warn("value must be 42", UserWarning)
2023-01-27 19:11:00 +08:00
...
2017-09-07 16:28:52 +08:00
2023-01-27 19:11:00 +08:00
>>> with warns(UserWarning, match=r"must be \d+$"):
2017-09-07 16:28:52 +08:00
... warnings.warn("this is not here", UserWarning)
2023-01-27 19:11:00 +08:00
...
2017-09-07 16:28:52 +08:00
Traceback (most recent call last):
...
2021-10-24 04:17:35 +08:00
Failed: DID NOT WARN. No warnings of type ...UserWarning... were emitted...
2017-03-21 10:35:01 +08:00
2023-01-27 19:11:00 +08:00
>>> with warns(UserWarning, match=re.escape("issue with foo() func")):
... warnings.warn("issue with foo() func")
...
2021-06-24 03:34:48 +08:00
You can also call :func: `pytest.warns` on a function or code string:
2017-03-21 10:35:01 +08:00
2019-08-07 04:25:54 +08:00
.. code-block :: python
2017-03-21 10:35:01 +08:00
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
2019-04-12 19:50:26 +08:00
additional information:
.. code-block :: python
2017-03-21 10:35:01 +08:00
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).
2021-06-12 22:38:24 +08:00
The :ref: `recwarn <recwarn>` fixture automatically ensures to reset the warnings
filter at the end of the test, so no global state is leaked.
2017-03-21 10:35:01 +08:00
.. _`recording warnings`:
.. _recwarn:
Recording warnings
2019-02-02 10:48:29 +08:00
------------------
2017-03-21 10:35:01 +08:00
2021-06-24 03:34:48 +08:00
You can record raised warnings either using :func: `pytest.warns` or with
2017-03-21 10:35:01 +08:00
the `` recwarn `` fixture.
2021-06-24 03:34:48 +08:00
To record with :func: `pytest.warns` without asserting anything about the warnings,
2021-05-15 22:15:43 +08:00
pass no arguments as the expected warning type and it will default to a generic Warning:
2019-04-12 19:50:26 +08:00
.. code-block :: python
2017-03-21 10:35:01 +08:00
2021-05-15 22:15:43 +08:00
with pytest.warns() as record:
2017-03-21 10:35:01 +08:00
warnings.warn("user", UserWarning)
warnings.warn("runtime", RuntimeWarning)
assert len(record) == 2
assert str(record[0].message) == "user"
assert str(record[1].message) == "runtime"
2019-04-12 19:50:26 +08:00
The `` recwarn `` fixture will record warnings for the whole function:
.. code-block :: python
2017-03-21 10:35:01 +08:00
import warnings
2019-04-12 19:50:26 +08:00
2017-03-21 10:35:01 +08:00
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
2021-06-24 03:34:48 +08:00
Both `` recwarn `` and :func: `pytest.warns` return the same interface for recorded
2017-03-21 10:35:01 +08:00
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
2018-02-28 05:26:40 +08:00
warnings, or index into it to get a particular recorded warning.
2017-03-21 10:35:01 +08:00
2020-09-06 16:52:30 +08:00
Full API: :class: `~_pytest.recwarn.WarningsRecorder` .
2017-03-21 10:35:01 +08:00
2022-01-14 02:32:22 +08:00
.. _`warns use cases`:
Additional use cases of warnings in tests
-----------------------------------------
Here are some use cases involving warnings that often come up in tests, and suggestions on how to deal with them:
2022-08-17 22:44:58 +08:00
- To ensure that **at least one** of the indicated warnings is issued, use:
2022-01-14 02:32:22 +08:00
.. code-block :: python
2022-08-17 22:44:58 +08:00
def test_warning():
with pytest.warns((RuntimeWarning, UserWarning)):
...
- To ensure that **only** certain warnings are issued, use:
.. code-block :: python
def test_warning(recwarn):
2022-01-27 20:32:37 +08:00
...
2022-08-17 22:44:58 +08:00
assert len(recwarn) == 1
user_warning = recwarn.pop(UserWarning)
assert issubclass(user_warning.category, UserWarning)
2022-01-14 02:32:22 +08:00
- To ensure that **no** warnings are emitted, use:
.. code-block :: python
2022-08-17 22:44:58 +08:00
def test_warning():
with warnings.catch_warnings():
warnings.simplefilter("error")
...
2022-01-14 02:32:22 +08:00
- To suppress warnings, use:
.. code-block :: python
with warnings.catch_warnings():
warnings.simplefilter("ignore")
2022-01-27 20:32:37 +08:00
...
2022-01-14 02:32:22 +08:00
2019-02-02 10:48:29 +08:00
.. _custom_failure_messages:
Custom failure messages
-----------------------
Recording warnings provides an opportunity to produce custom test
failure messages for when no warnings are issued or other conditions
are met.
.. code-block :: python
def test():
with pytest.warns(Warning) as record:
f()
if not record:
2019-02-06 05:04:26 +08:00
pytest.fail("Expected a warning!")
2019-02-02 10:48:29 +08:00
If no warnings are issued when calling `` f `` , then `` not record `` will
2020-11-19 22:44:59 +08:00
evaluate to `` True `` . You can then call :func: `pytest.fail` with a
2019-02-02 10:48:29 +08:00
custom error message.
2018-09-15 02:58:22 +08:00
.. _internal-warnings:
2018-09-04 00:36:12 +08:00
Internal pytest warnings
------------------------
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
2018-11-24 13:41:22 +08:00
.. code-block :: pytest
2018-09-04 00:36:12 +08:00
$ pytest test_pytest_warnings.py -q
2018-09-06 05:06:32 +08:00
============================= warnings summary =============================
2018-10-27 21:07:54 +08:00
test_pytest_warnings.py:1
2021-10-04 14:56:26 +08:00
/home/sweet/project/test_pytest_warnings.py:1: PytestCollectionWarning: cannot collect test class 'Test' because it has a __init__ constructor (from: test_pytest_warnings.py)
2018-10-27 21:07:54 +08:00
class Test:
2018-09-04 00:36:12 +08:00
2021-07-06 15:09:04 +08:00
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
2019-11-19 23:43:51 +08:00
1 warning in 0.12s
2018-09-04 00:36:12 +08:00
These warnings might be filtered using the same builtin mechanisms used to filter other types of warnings.
2018-09-15 02:58:22 +08:00
Please read our :ref: `backwards-compatibility` to learn how we proceed about deprecating and eventually removing
features.
2018-09-04 00:36:12 +08:00
2020-07-23 08:36:51 +08:00
The full list of warnings is listed in :ref: `the reference documentation <warnings ref>` .
2022-02-15 21:00:50 +08:00
.. _`resource-warnings`:
Resource Warnings
-----------------
Additional information of the source of a :class: `ResourceWarning` can be obtained when captured by pytest if
:mod: `tracemalloc` module is enabled.
One convenient way to enable :mod: `tracemalloc` when running tests is to set the :envvar: `PYTHONTRACEMALLOC` to a large
enough number of frames (say `` 20 `` , but that number is application dependent).
For more information, consult the `Python Development Mode <https://docs.python.org/3/library/devmode.html> `__
section in the Python documentation.