diff --git a/_pytest/warnings.py b/_pytest/warnings.py index 915862a9d..1b3c8387e 100644 --- a/_pytest/warnings.py +++ b/_pytest/warnings.py @@ -59,6 +59,11 @@ def catch_warnings_for_item(item): for arg in inifilters: _setoption(warnings, arg) + mark = item.get_marker('filterwarnings') + if mark: + for arg in mark.args: + warnings._setoption(arg) + yield for warning in log: diff --git a/changelog/2598.feature b/changelog/2598.feature new file mode 100644 index 000000000..b811b9120 --- /dev/null +++ b/changelog/2598.feature @@ -0,0 +1,2 @@ +Introduced ``@pytest.mark.filterwarnings`` mark which allows overwriting the warnings filter on a per test, class or module level. +See the `docs `_ for more information. diff --git a/doc/en/warnings.rst b/doc/en/warnings.rst index 34dc1ece0..c84277173 100644 --- a/doc/en/warnings.rst +++ b/doc/en/warnings.rst @@ -78,6 +78,40 @@ Both ``-W`` command-line option and ``filterwarnings`` ini option are based on P `-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 diff --git a/testing/test_warnings.py b/testing/test_warnings.py index 858932846..58e6bcf74 100644 --- a/testing/test_warnings.py +++ b/testing/test_warnings.py @@ -188,3 +188,32 @@ def test_works_with_filterwarnings(testdir): result.stdout.fnmatch_lines([ '*== 1 passed in *', ]) + + +@pytest.mark.parametrize('default_config', ['ini', 'cmdline']) +def test_filterwarnings_mark(testdir, default_config): + """ + Test ``filterwarnings`` mark works and takes precedence over command line and ini options. + """ + if default_config == 'ini': + testdir.makeini(""" + [pytest] + filterwarnings = always + """) + testdir.makepyfile(""" + import warnings + import pytest + + @pytest.mark.filterwarnings('ignore::RuntimeWarning') + def test_ignore_runtime_warning(): + warnings.warn(RuntimeWarning()) + + @pytest.mark.filterwarnings('error') + def test_warning_error(): + warnings.warn(RuntimeWarning()) + + def test_show_warning(): + warnings.warn(RuntimeWarning()) + """) + result = testdir.runpytest('-W always' if default_config == 'cmdline' else '') + result.stdout.fnmatch_lines(['*= 1 failed, 2 passed, 1 warnings in *'])