8、pytest -- 捕获告警信息
pytest 3.1版本新增特性
1. 告警信息的默认捕获行为
pytest可以自动捕获测试中产生的告警信息,并在执行结束后进行展示;
下面这个例子,我们在测试中人为的产生一条告警:
# 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):- λ pipenv run 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):- λ pipenv run 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):- λ pipenv run 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的配置为:- # src/chapter-8/pytest.ini [pytest]
 filterwarnings =
 ignore
 default:::test_show_warnings
 - 不带 - -W选项执行:- λ pipenv run 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,禁止捕获由它所触发的用户告警:
# src/chapter-8/test_show_warning.py
@pytest.mark.filterwarnings('ignore::UserWarning')
def test_two():
    assert api_v1() == 1
执行这个用例:
λ pipenv run 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变量为整个测试模块中所有的用例添加告警过滤器;例如,将模块中所有的告警转换为异常处理:
pytestmark = pytest.mark.filterwarnings("error")
3. 去使能告警信息的展示
我们可以通过--disable-warnings命令行选项来禁止告警信息的展示;例如,我们在测试输出中不展示test_one用例所产生到的告警信息:
λ pipenv run 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命令行选项彻底禁止告警的捕获行为:
λ pipenv run 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 =======================
如果你足够细心的话,你可以看到它们的区别:
================ 1 passed, 1 deselected, 1 warnings in 0.03s =================
和
====================== 1 passed, 1 deselected in 0.03s =======================
5. DeprecationWarning和PendingDeprecationWarning告警
遵循PEP-0565的建议,pytest会默认捕获DeprecationWarning和PendingDeprecationWarning类型的告警;
有时候,你并不需要这种行为,可以通过在pytest.ini添加配置;例如,忽略告警信息匹配".*U.*mode is deprecated"的DeprecationWarning告警:
[pytest]
filterwarnings =
    ignore:.*U.*mode is deprecated:DeprecationWarning
注意:
如果已经在
python解释器中配置了告警选项,那么pytest不会再添加任何默认的告警过滤器;这一点,可以在pytest的源码中得到证实:# _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 #2908:https://github.com/pytest-dev/pytest/issues/2908
5.1. pytest.deprecated_call方法
我们可以通过deprecated_call方法确保一段代码触发了DeprecationWarning或PendingDeprecationWarning告警:
# 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也支持上下文管理器的写法,所以上面的例子也可以写成:
def test_deprecation():
    with pytest.deprecated_call():
        assert api_call_v1() == 200
6. 编写触发期望告警的断言
我们可以使用pytest.warns()作为上下文管理器,来编写一个触发期望告警的断言,它和pytest.raises()的用法很接近;
在正式开始之前,我们来看一下上一节中deprecated_call方法的源码:
# _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,判断捕获到的告警信息是否匹配既定的正则表达式:- def test_deprecation():
 with pytest.warns((DeprecationWarning, PendingDeprecationWarning), match=r'v1版本已废弃'):
 assert api_call_v1() == 200
 
- 我们也可以直接传递可调用对象,表达式返回执行这个可调用对象的结果: - def test_deprecation():
 assert pytest.warns((DeprecationWarning, PendingDeprecationWarning), api_call_v1, match=r'和 pytest.raises() 方法一样,这时 pytest 不再判断告警信息是否正确') == 200- 注意:和 - pytest.raises()一样,此时- match参数不再生效;
- pytest.warns()可以返回一个列表,包含所有捕获到的告警对象(- warnings.WarningMessage):- 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()的源码一起看,上面所有的用法和特性都可以体现在里面:# _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. 自定义失败时的提示消息
当我们使用一段代码,期望其触发告警时,我们可以通过一下方法,自定义失败时的提示消息,增加其可读性:
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,它可以记录用例产生的所有的告警;
同样,重写之前的例子来说明:
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
GitHub仓库地址:https://github.com/luizyao/pytest-chinese-doc
8、pytest -- 捕获告警信息的更多相关文章
- Python中的Warnings模块忽略告警信息
		写了个小工具,其中涉及到从远程数据库中查询并返回,数据库是utf8编码,但是我的工具用的是GB2312编码,因此在返回数据的时候,有部分数据出现了:Truncated incorrect DECIMA ... 
- PROCEDURE_监测系统_告警信息存储过程—产生告警信息插入告警表
		create or replace procedure proc_alarmlog(in_id in number, --采集器编码 ... 
- zabbix使用企业微信发送告警信息
		用qq邮箱发送告警信息一点都不方便,看到网上说也可以使用微信发送告警信息,所以就试了一下. 首先先试着在虚拟主机上给微信发送信息. 我们需要注册企业微信,注册时有一个地方需要注意,就是注册时选择组织, ... 
- 一个简单好用的zabbix告警信息发送工具
		之前使用邮件和短信发送zabbix告警信息,但告警信息无法实时查看或者无法发送,故障无法及时通知运维人员. 后来使用第三方微信接口发送信息,愉快地用了一年多,突然收费了. zabbix告警一直是我的痛 ... 
- [置顶]
        zabbix通过lykchat发送告警信息配置过程
		本文介绍zabbix通过lykchat发送告警信息配置过程. lykchat代码在https://github.com/lykops/lykchat/ 步骤 编写脚本 1).查看服务器端的配置文件et ... 
- [置顶]
        个人微信号发送zabbix告警信息
		之前使用邮件和短信发送zabbix告警信息,但告警信息无法实时查看或者无法发送,故障无法及时通知运维人员. 后来使用第三方微信接口发送信息,愉快地用了一年多,突然收费了. zabbix告警一直是我的痛 ... 
- [置顶]
        一个简单好用的zabbix告警信息发送工具
		之前使用邮件和短信发送zabbix告警信息,但告警信息无法实时查看或者无法发送,故障无法及时通知运维人员. 后来使用第三方微信接口发送信息,愉快地用了一年多,突然收费了. zabbix告警一直是我的痛 ... 
- [置顶]
        zabbix告警信息-lykchat信息发送系统
		lykchat信息发送系统 lykchat信息发送系统是Python3开发的,通过模拟微信网页端,基于个人微信号,为系统管理人员提供信息发送工具. 实现的功能有用户登录管理.微信登陆管理和微信信息发送 ... 
- Python 操作Zabbix API 获取ERROR级别告警信息并打印
		1.需求:有一个语音合成播报项目,要实时获取zabbix的ERROR级别以上告警信息,将该信息合成语音播报出去.(合成语音及播报已经完成) 2.现实:整理zabbix告警级别,将不太重要的告警放到ER ... 
随机推荐
- 如何提高scroll事件的性能
			1. chrome devtool 是诊断页面滚动性能的有效工具 2. 提升滚动时性能,就是要达到fps高且稳. 3. 具体可以从以下方面着手 使用web worker分离无页面渲染无关的逻辑计算 触 ... 
- 异步处理ServletRequest引发的血案
			我们的APP生产上出了一次比较严重的事故,许多用户投诉登录后能看到别人的信息,收到投诉后我们就开始查找问题,一般这样的问题都是线程安全引起的,所以查找原因的思路也是按线程安全的思路去查. 业务场景是这 ... 
- 10个值得深思的_PHP_面试问题
			Q1 第一个问题关于弱类型 $str1 = 'yabadabadoo'; $str2 = 'yaba'; if (strpos($str1,$str2)) { echo "\"&q ... 
- 《Windows内核分析》专题-索引目录
			该篇博客整理了<Windows内核分析>专题的各篇博文,方便查找. 一.保护模式 二.进程与线程 [Windows内核分析]KPCR结构体介绍 (CPU控制区 Processor Cont ... 
- 编程杂谈——std::vector与List<T>的性能比较
			昨天在比较完C++中std::vector的两个方法的性能差异并留下记录后--编程杂谈--使用emplace_back取代push_back,今日尝试在C#中测试对应功能的性能. C#中对应std:: ... 
- 小白学 Python(1):开篇
			人生苦短,我用 Python 引言 大家好,可能大家都对我比较熟悉了,不熟悉请去面壁(现在熟悉一下也来得及)~ 简单做一个自我介绍,我是极客挖掘机的唯一作者,一位油腻的 Java 程序员[臭鸡蛋什么的 ... 
- Error response from daemon: Get https://registry-1.docker.io/v2/library/nginx/manifests/1.14-alpine: Get https://auth.docker.io/token?scope=repository%3Alibrary%2Fnginx%3Apull&service=registry.docker.
			docker pull 镜像时报错: Error response from daemon: Get https://registry-1.docker.io/v2/library/nginx/man ... 
- VMware ESXI6.0服务器安装系列:RAID设置
			本文转载至http://www.scriptjc.com/article/847 1.连接显示器 2.插上键盘.U盘.显示器 3.看显示屏上的提示,按F12关机 4.关机前输入密码,然后按回车键 更换 ... 
- Flask中的CBV以及正则表达式
			Flask中的CBV以及正则表达式 一.CBV def auth(func): def inner(*args, **kwargs): print('before') result = func(*a ... 
- Sublime Text3工具的安装、破解、VIM功能vintage插件教程(已经实践、绝对可用)
			工欲善其事,必先利其器.Sublime Text是一款很好的开发工具,开发php项目很好用,尤其是Sublime Text的一些插件功能,可以享用VIM的快捷编辑和html.js等自动补全 ... 
