pytest中文使用文档----8捕获告警信息

  • [1. 告警信息的默认捕获行为](#1. 告警信息的默认捕获行为)
  • [2. @pytest.mark.filterwarnings](#2. @pytest.mark.filterwarnings)
  • [3. 去使能告警信息的展示](#3. 去使能告警信息的展示)
  • [4. 去使能告警的捕获行为](#4. 去使能告警的捕获行为)
  • [5. DeprecationWarningPendingDeprecationWarning告警](#5. DeprecationWarning和PendingDeprecationWarning告警)
    • [5.1. pytest.deprecated_call方法](#5.1. pytest.deprecated_call方法)
  • [6. 编写触发期望告警的断言](#6. 编写触发期望告警的断言)
    • [6.1. 自定义失败时的提示消息](#6.1. 自定义失败时的提示消息)
  • [7. recwarn fixture](#7. recwarn fixture)
  • [8. pytest自定义的告警类型](#8. pytest自定义的告警类型)

pytest 3.1版本新增特性

1. 告警信息的默认捕获行为

pytest可以自动捕获测试中产生的告警信息,并在执行结束后进行展示;

下面这个例子,我们在测试中人为的产生一条告警:

python 复制代码
# src/chapter-8/test_show_warning.py

import warnings


def api_v1():
    warnings.warn(UserWarning('请使用新版本的API。'))
    return 1


def test_one():
    assert api_v1() == 1

我们也可以通过-W arg命令行选项来自定义告警的捕获行为:

arg参数的格式为:action:message:category:module:lineno

  • action只能在"error", "ignore", "always(all)", "default", "module", "once"中取值,默认取值为default
  • category必须是Warning的子类,默认取值为Warning类,表示所有的告警;
  • module必须为字符串,表示特定模块产生的告警信息;

下面是一些常见的使用场景:

  • 忽略某一种类型的告警信息;例如,忽略UserWarning类型的告警(-W ignore::UserWarning):

    bash 复制代码
    λ pytest -W ignore::UserWarning src/chapter-8/test_show_warnings.py
    ============================ test session starts ============================= 
    platform win32 -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0
    rootdir: D:\Personal Files\Projects\pytest-chinese-doc
    collected 1 item
    
    src\chapter-8\test_show_warnings.py .                                   [100%]
    
    ============================= 1 passed in 0.02s ==============================
  • 将某一种类型的告警转换为异常来处理;例如,将UserWarning告警转换为异常处理(-W error::UserWarning):

    bash 复制代码
    λ pytest -W error::UserWarning src/chapter-8/test_show_warnings.py
    
    ============================ test session starts ============================= 
    platform win32 -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0
    rootdir: D:\Personal Files\Projects\pytest-chinese-doc
    collected 1 item
    
    src\chapter-8\test_show_warnings.py F                                   [100%]
    
    ================================== FAILURES ================================== 
    __________________________________ test_one __________________________________
    
        def test_one():
    >       assert api_v1() == 1
    
    src\chapter-8\test_show_warnings.py:31:
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    
        def api_v1():
    >       warnings.warn(UserWarning('请使用新版本的API。'))
    E       UserWarning: 请使用新版本的API。
    
    src\chapter-8\test_show_warnings.py:26: UserWarning
    ============================= 1 failed in 0.05s ==============================
  • 只展示某一个模块中产生的告警;例如,只展示test_show_warnings模块产生的告警,忽略其它所有的告警(-W ignore -W default:::test_show_warnings):

    bash 复制代码
    λ pytest -W ignore -W default:::test_show_warnings src/chapter-8/
    ============================ test session starts ============================= 
    platform win32 -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0
    rootdir: D:\Personal Files\Projects\pytest-chinese-doc
    collected 1 item
    
    src\chapter-8\test_show_warnings.py .                                   [100%]
    
    ============================== warnings summary ============================== 
    src/chapter-8/test_show_warnings.py::test_one
      D:\Personal Files\Projects\pytest-chinese-doc\src\chapter-8\test_show_warnings.py:26: UserWarning: 请使用新版本的API。
        warnings.warn(UserWarning('请使用新版本的API。'))
    
    -- Docs: https://docs.pytest.org/en/latest/warnings.html
    ======================= 1 passed, 1 warnings in 0.03s ========================

    这里我们演示了多个-W选项的组合操作,优先级是从左到右依次递增的;这里如果将它们调换一下顺序(即-W default:::test_show_warnings -W ignore),因为-W ignore最后生效,覆盖掉之前的操作,最终的结果就是我们一个告警信息都没有捕获到;

  • 我们也可以通过在pytest.ini文件中配置filterwarnings项,来实现同样的效果;例如,上述的例子在pytest.ini的配置为:

    ini 复制代码
    # src/chapter-8/pytest.ini
    
    [pytest]
    filterwarnings =
        ignore
        default:::test_show_warnings

    不带-W选项执行:

    bash 复制代码
    λ pytest src/chapter-8/
    ============================ test session starts ============================= 
    platform win32 -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0
    rootdir: D:\Personal Files\Projects\pytest-chinese-doc\src\chapter-8, inifile: pytest.ini
    collected 1 item
    
    src\chapter-8\test_show_warnings.py .                                   [100%]
    
    ============================== warnings summary ============================== 
    test_show_warnings.py::test_one
      D:\Personal Files\Projects\pytest-chinese-doc\src\chapter-8\test_show_warnings.py:26: UserWarning: 请使用新版本的API。
        warnings.warn(UserWarning('请使用新版本的API。'))
    
    -- Docs: https://docs.pytest.org/en/latest/warnings.html
    ======================= 1 passed, 1 warnings in 0.04s ========================

-W其实是python本身自带的命令行选项,你可以通过访问官方文档以了解更多:https://docs.python.org/3.7/library/warnings.html#warning-filter

2. @pytest.mark.filterwarnings

上述操作我们是在命令行上实现的,如果想要在用例、类甚至是模块级别上自定义告警的捕获行为,上面的方法就不是很便利了;这里,我们可以通过为测试项添加告警过滤器来实现这种需求;

还记得在上一章中pytest.ini中的配置吗?我们禁止了除test_show_warnings模块外,其它所有告警的捕获行为;现在,我们在这个模块中新加一个用例test_two,禁止捕获由它所触发的用户告警:

python 复制代码
# src/chapter-8/test_show_warning.py

@pytest.mark.filterwarnings('ignore::UserWarning')
def test_two():
    assert api_v1() == 1

执行这个用例:

bash 复制代码
λ pytest -k "test_two" src/chapter-8/
============================ test session starts ============================= 
platform win32 -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0
rootdir: D:\Personal Files\Projects\pytest-chinese-doc\src\chapter-8, inifile: pytest.ini
collected 2 items / 1 deselected / 1 selected

src\chapter-8\test_show_warnings.py .                                   [100%]

====================== 1 passed, 1 deselected in 0.03s =======================

我们没有捕获任何告警信息,这说明通过@pytest.mark.filterwarnings添加的过滤器优先级要高于命令行或pytest.ini添加的过滤器;你也可以通过执行test_one用例来对比它们之间的不同;

我们可以通过将@pytest.mark.filterwarnings应用于测试类来为这个类中所有的用例添加告警过滤器;

也可以通过设置pytestmark变量为整个测试模块中所有的用例添加告警过滤器;例如,将模块中所有的告警转换为异常处理:

python 复制代码
pytestmark = pytest.mark.filterwarnings("error")

3. 去使能告警信息的展示

我们可以通过--disable-warnings命令行选项来禁止告警信息的展示;例如,我们在测试输出中不展示test_one用例所产生到的告警信息:

bash 复制代码
λ pytest -k "test_one" --disable-warnings src/chapter-8/
============================ test session starts ============================= 
platform win32 -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0
rootdir: D:\Personal Files\Projects\pytest-chinese-doc\src\chapter-8, inifile: pytest.ini
collected 2 items / 1 deselected / 1 selected

src\chapter-8\test_show_warnings.py .                                   [100%]

================ 1 passed, 1 deselected, 1 warnings in 0.03s =================

4. 去使能告警的捕获行为

上一章我们只是不展示捕获到的告警信息,这里我们可以通过-p no:warnings命令行选项彻底禁止告警的捕获行为:

bash 复制代码
λ pytest -k "test_one" -p no:warnings src/chapter-8/
============================ test session starts ============================= 
platform win32 -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0
rootdir: D:\Personal Files\Projects\pytest-chinese-doc\src\chapter-8, inifile: pytest.ini
collected 2 items / 1 deselected / 1 selected

src\chapter-8\test_show_warnings.py .                                   [100%]

====================== 1 passed, 1 deselected in 0.03s =======================

如果你足够细心的话,你可以看到它们的区别:

bash 复制代码
================ 1 passed, 1 deselected, 1 warnings in 0.03s =================

bash 复制代码
====================== 1 passed, 1 deselected in 0.03s =======================

5. DeprecationWarningPendingDeprecationWarning告警

遵循PEP-0565的建议,pytest会默认捕获DeprecationWarningPendingDeprecationWarning类型的告警;

有时候,你并不需要这种行为,可以通过在pytest.ini添加配置;例如,忽略告警信息匹配".*U.*mode is deprecated"DeprecationWarning告警:

ini 复制代码
[pytest]
filterwarnings =
    ignore:.*U.*mode is deprecated:DeprecationWarning

注意:

如果已经在python解释器中配置了告警选项,那么pytest不会再添加任何默认的告警过滤器;这一点,可以在pytest的源码中得到证实:

python 复制代码
# _pytest/warnings.py

 if not sys.warnoptions:
        # if user is not explicitly configuring warning filters, show deprecation warnings by default (#2908)
        warnings.filterwarnings("always", category=DeprecationWarning)
        warnings.filterwarnings("always", category=PendingDeprecationWarning)

pytest issue #2908https://github.com/pytest-dev/pytest/issues/2908

5.1. pytest.deprecated_call方法

我们可以通过deprecated_call方法确保一段代码触发了DeprecationWarningPendingDeprecationWarning告警:

python 复制代码
# src/chapter-8/test_deprecation.py

import warnings
import pytest


def api_call_v1():
    warnings.warn('v1版本已废弃,请使用v2版本的api;', DeprecationWarning)
    return 200


def test_deprecation():
    assert pytest.deprecated_call(api_call_v1) == 200

同时,deprecated_call也支持上下文管理器的写法,所以上面的例子也可以写成:

python 复制代码
def test_deprecation():
    with pytest.deprecated_call():
        assert api_call_v1() == 200

6. 编写触发期望告警的断言

我们可以使用pytest.warns()作为上下文管理器,来编写一个触发期望告警的断言,它和pytest.raises()的用法很接近;

在正式开始之前,我们来看一下上一节中deprecated_call方法的源码:

python 复制代码
# _pytest/warnings.py

def deprecated_call(func=None, *args, **kwargs):
    __tracebackhide__ = True
    if func is not None:
        args = (func,) + args
    return warns((DeprecationWarning, PendingDeprecationWarning), *args, **kwargs)

可以看到,deprecated_call也不过是pytest.warns()的封装,区别在于其指定了具体期望的告警类型;

现在,我们来具体看一下pytest.warns()的用法(以上一节的例子说明):

  • 我们可以为其传递一个关键字参数match,判断捕获到的告警信息是否匹配既定的正则表达式:

    python 复制代码
    def test_deprecation():
        with pytest.warns((DeprecationWarning, PendingDeprecationWarning), match=r'v1版本已废弃'):
            assert api_call_v1() == 200
  • 我们也可以直接传递可调用对象,表达式返回执行这个可调用对象的结果:

    python 复制代码
    def test_deprecation():
        assert pytest.warns((DeprecationWarning, PendingDeprecationWarning), api_call_v1, match=r'和 pytest.raises() 方法一样,这时 pytest 不再判断告警信息是否正确') == 200

    注意:和pytest.raises()一样,此时match参数不再生效;

  • pytest.warns()可以返回一个列表,包含所有捕获到的告警对象(warnings.WarningMessage):

    python 复制代码
    import re
    
    def test_deprecation():
        with pytest.warns((DeprecationWarning, PendingDeprecationWarning)) as records:
            assert api_call_v1() == 200
        assert len(records) == 1
        assert re.search(r'v1版本已废弃', records[0].message.args[0])

    实际上,其返回的并不是一个列表,只是实现了__getitem__()__len__()方法的普通类,其内部本身有一个_list的私有属性用于存储所有的数据;

学习这一章节最好的办法就是结合pytest.warns()的源码一起看,上面所有的用法和特性都可以体现在里面:

python 复制代码
# _pytest/recwarn.py

def warns(
 expected_warning: Union["Type[Warning]", Tuple["Type[Warning]", ...]],
 *args: Any,
 match: Optional[Union[str, "Pattern"]] = None,
 **kwargs: Any
) -> Union["WarningsChecker", Any]:

 __tracebackhide__ = True
 if not args:
     if kwargs:
         msg = "Unexpected keyword arguments passed to pytest.warns: "
         msg += ", ".join(sorted(kwargs))
         msg += "\nUse context-manager form instead?"
         raise TypeError(msg)
     return WarningsChecker(expected_warning, match_expr=match)
 else:
     func = args[0]
     if not callable(func):
         raise TypeError(
             "{!r} object (type: {}) must be callable".format(func, type(func))
         )
     with WarningsChecker(expected_warning):
         return func(*args[1:], **kwargs)

6.1. 自定义失败时的提示消息

当我们使用一段代码,期望其触发告警时,我们可以通过以下方法,自定义失败时的提示消息,增加其可读性:

python 复制代码
def test_deprecation():
    with pytest.warns(Warning) as records:
        rsp = api_call_v1()
        if not records:
            pytest.fail('期望 api_call_v1 触发一个告警,实际上没有;')
        assert rsp == 200

如果api_call_v1没有触发任何告警,pytest就会显示pytest.fail中自定义的提示消息;

7. recwarn fixture

上一章的最后,我们通过接收pytest.warns()的返回值来记录捕获到的所有告警;在这一章,我们可以通过recwarn来实现同样的功能;

recwarn是一个用例级别的fixture,它可以记录用例产生的所有的告警;

同样,重写之前的例子来说明:

python 复制代码
import re

def test_deprecation(recwarn):
    api_call_v1()
    assert len(recwarn) == 1
    w = recwarn.pop()  # 不指定告警类型的话,默认弹出最先捕获的告警
    assert issubclass(w.category, (DeprecationWarning, PendingDeprecationWarning))
    assert re.search(r'v1版本已废弃', w.message.args[0])

recwarn和之前pytest.warns()返回值一样,都是一个WarningsRecorder的实例;

8. pytest自定义的告警类型

pytest本身封装了一些告警的类型,并作为公共接口以供用户使用;

下面列举了一些常见的内部告警:

告警 父类 描述
PytestWarning UserWarning 所有告警的父类;
PytestCollectionWarning PytestWarning 不能够收集某个模块中的用例;
PytestConfigWarning PytestWarning 配置错误;
PytestUnknownMarkWarning PytestWarning 使用了未知的标记;

更多的内部告警可以查看:https://docs.pytest.org/en/5.1.3/warnings.html#pytest.PytestWarning

相关推荐
崔庆才丨静觅2 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60613 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了3 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅3 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅4 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅4 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment4 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊5 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax