捕获警告

从版本开始 3.1 ,pytest现在在测试执行期间自动捕获警告,并在会话结束时显示这些警告:

# 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

现在运行pytest将生成以下输出:

$ pytest test_show_warnings.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 1 item

test_show_warnings.py .                                              [100%]

============================= warnings summary =============================
test_show_warnings.py::test_one
  $REGENDOC_TMPDIR/test_show_warnings.py:5: UserWarning: api v1, should use functions from v2
    warnings.warn(UserWarning("api v1, should use functions from v2"))

-- Docs: https://docs.pytest.org/en/stable/warnings.html
======================= 1 passed, 1 warning in 0.12s =======================

这个 -W 可以将标志传递给控制将显示哪些警告,甚至将其转换为错误:

$ 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:10:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    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:5: UserWarning
========================= short test summary info ==========================
FAILED test_show_warnings.py::test_one - UserWarning: api v1, should use ...
1 failed in 0.12s

相同的选项可以在 pytest.inipyproject.toml 使用文件 filterwarnings ini选项。例如,下面的配置将忽略与正则表达式匹配的所有用户警告和特定的弃用警告,但会将所有其他警告转换为错误。

# pytest.ini
[pytest]
filterwarnings =
    error
    ignore::UserWarning
    ignore:function ham\(\) is deprecated:DeprecationWarning
# 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',
]

当警告与列表中的多个选项匹配时,将执行最后一个匹配选项的操作。

两个 -W 命令行选项和 filterwarnings ini选项基于python自己的 -W optionwarnings.simplefilter ,因此请参阅Python文档中的这些部分,以了解其他示例和高级用法。

@pytest.mark.filterwarnings

你可以使用 @pytest.mark.filterwarnings 要向特定测试项添加警告筛选器,允许您更好地控制应在测试、类甚至模块级别捕获哪些警告:

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

使用标记应用的筛选器优先于命令行上传递的或由 filterwarnings ini选项。

通过使用 filterwarnings 通过设置 pytestmark 变量:

# turns all warnings into errors for this module
pytestmark = pytest.mark.filterwarnings("error")

学分授予Florian Schulze,以便在 pytest-warnings 插件。

禁用警告摘要

虽然不推荐,但您可以使用 --disable-warnings 命令行选项,用于完全禁止来自测试运行输出的警告摘要。

完全禁用警告捕获

此插件在默认情况下启用,但可以在 pytest.ini 文件与:

[pytest]
addopts = -p no:warnings

或通过 -p no:warnings 在命令行中。如果测试套件使用外部系统处理警告,这可能很有用。

弃用警告和未决弃用警告

默认情况下,pytest将显示 DeprecationWarningPendingDeprecationWarning 用户代码和第三方库发出的警告 PEP-0565 . 这有助于用户保持代码的现代性,并在有效删除不推荐使用的警告时避免损坏代码。

有时,隐藏一些在您无法控制的代码(如第三方库)中发生的特定拒绝警告很有用,在这种情况下,您可以使用警告筛选选项(ini或标记)忽略这些警告。

例如:

[pytest]
filterwarnings =
    ignore:.*U.*mode is deprecated:DeprecationWarning

这将忽略所有类型的警告 DeprecationWarning 消息的开头与正则表达式匹配的位置 ".*U.*mode is deprecated" .

注解

如果在解释器级别配置了警告,请使用 PYTHONWARNINGS 环境变量或 -W 命令行选项,pytest在默认情况下不会配置任何过滤器。

同样,Pytest不遵循 PEP-0506 建议重置所有警告筛选器,因为它可能会破坏通过调用 warnings.simplefilter (见问题) #2430 举个例子)。

确保代码触发拒绝警告

您也可以使用 pytest.deprecated_call() 用于检查某个函数调用是否触发 DeprecationWarningPendingDeprecationWarning

import pytest


def test_myfunction_deprecated():
    with pytest.deprecated_call():
        myfunction(17)

此测试将失败,如果 myfunction 调用时不发出弃用警告 17 争论。

默认情况下, DeprecationWarningPendingDeprecationWarning 使用时不会被捕获 pytest.warns()recwarn 因为默认的Python警告过滤器会隐藏它们。如果您希望在自己的代码中记录它们,请使用 warnings.simplefilter('always')

import warnings
import pytest


def test_deprecation(recwarn):
    warnings.simplefilter("always")
    myfunction(17)
    assert len(recwarn) == 1
    assert recwarn.pop(DeprecationWarning)

这个 recwarn fixture自动确保在测试结束时重置warnings过滤器,因此不会泄漏全局状态。

使用警告功能断言警告

您可以使用func:pytest.warns`检查代码是否引发了特定的警告,其工作方式类似于 :ref:`raises <assertraises>

import warnings
import pytest


def test_warning():
    with pytest.warns(UserWarning):
        warnings.warn("my warning", UserWarning)

如果未发出相关警告,测试将失败。关键字参数 match 要断言异常与文本或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...

也可以对函数或代码串调用func:pytest.warns

pytest.warns(expected_warning, func, *args, **kwargs)
pytest.warns(expected_warning, "func(*args, **kwargs)")

函数还返回所有引发的警告的列表(如 warnings.WarningMessage 对象),可以查询其他信息:

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"

或者,您可以使用 recwarn 夹具(见下文)。

注解

DeprecationWarningPendingDeprecationWarning 被区别对待;见 确保代码触发拒绝警告 .

录制警告

您可以使用func:pytest.warns`或使用 ``recwarn` 固定装置。

要使用func:pytest.warns`记录而不断言任何有关警告的内容,请传递 ``None` 作为预期的警告类型:

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"

这个 recwarn 夹具将记录整个功能的警告:

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

两个 recwarn 和func:pytest.warns`为录制的警告返回相同的接口:一个WarningsRecorder实例。要查看记录的警告,可以迭代此实例,调用 ``len` 在它上面获取记录的警告数,或者索引到它上面获取特定的记录警告。

完整的API: WarningsRecorder .

自定义失败消息

当没有发出警告或满足其他条件时,记录警告提供了生成自定义测试失败消息的机会。

def test():
    with pytest.warns(Warning) as record:
        f()
        if not record:
            pytest.fail("Expected a warning!")

如果调用时未发出警告 f 然后 not record 将评估为 True . 然后你可以打电话 pytest.fail() 带有自定义错误消息。

内部Pytest警告

在某些情况下,Pytest可能会生成自己的警告,例如使用不当或不推荐使用的特性。

例如,如果Pytest遇到匹配的类,它将发出警告。 python_classes 但也定义了 __init__ 构造函数,因为这样可以防止类被实例化:

# content of test_pytest_warnings.py
class Test:
    def __init__(self):
        pass

    def test_foo(self):
        assert 1 == 1
$ pytest test_pytest_warnings.py -q

============================= warnings summary =============================
test_pytest_warnings.py:1
  $REGENDOC_TMPDIR/test_pytest_warnings.py:1: PytestCollectionWarning: cannot collect test class 'Test' because it has a __init__ constructor (from: test_pytest_warnings.py)
    class Test:

-- Docs: https://docs.pytest.org/en/stable/warnings.html
1 warning in 0.12s

可以使用用于筛选其他类型警告的相同内置机制筛选这些警告。

请阅读我们的 向后兼容策略 要了解我们如何继续取消预测并最终删除功能。

中列出了警告的完整列表 the reference documentation .