pytest 3.1版本新增特性

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

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

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

  1. # src/chapter-8/test_show_warning.py
  2. import warnings
  3. def api_v1():
  4. warnings.warn(UserWarning('请使用新版本的API。'))
  5. return 1
  6. def test_one():
  7. 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):

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

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

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

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

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

    1. # src/chapter-8/pytest.ini
    2. [pytest]
    3. filterwarnings =
    4. ignore
    5. default:::test_show_warnings

    不带-W选项执行:

    1. λ pipenv run pytest src/chapter-8/
    2. ============================ test session starts =============================
    3. platform win32 -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0
    4. rootdir: D:\Personal Files\Projects\pytest-chinese-doc\src\chapter-8, inifile: pytest.ini
    5. collected 1 item
    6. src\chapter-8\test_show_warnings.py . [100%]
    7. ============================== warnings summary ==============================
    8. test_show_warnings.py::test_one
    9. D:\Personal Files\Projects\pytest-chinese-doc\src\chapter-8\test_show_warnings.py:26: UserWarning: 请使用新版本的API
    10. warnings.warn(UserWarning('请使用新版本的API。'))
    11. -- Docs: https://docs.pytest.org/en/latest/warnings.html
    12. ======================= 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,禁止捕获由它所触发的用户告警:

  1. # src/chapter-8/test_show_warning.py
  2. @pytest.mark.filterwarnings('ignore::UserWarning')
  3. def test_two():
  4. assert api_v1() == 1

执行这个用例:

  1. λ pipenv run pytest -k "test_two" src/chapter-8/
  2. ============================ test session starts =============================
  3. platform win32 -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0
  4. rootdir: D:\Personal Files\Projects\pytest-chinese-doc\src\chapter-8, inifile: pytest.ini
  5. collected 2 items / 1 deselected / 1 selected
  6. src\chapter-8\test_show_warnings.py . [100%]
  7. ====================== 1 passed, 1 deselected in 0.03s =======================

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

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

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

  1. pytestmark = pytest.mark.filterwarnings("error")

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

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

  1. λ pipenv run pytest -k "test_one" --disable-warnings src/chapter-8/
  2. ============================ test session starts =============================
  3. platform win32 -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0
  4. rootdir: D:\Personal Files\Projects\pytest-chinese-doc\src\chapter-8, inifile: pytest.ini
  5. collected 2 items / 1 deselected / 1 selected
  6. src\chapter-8\test_show_warnings.py . [100%]
  7. ================ 1 passed, 1 deselected, 1 warnings in 0.03s =================

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

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

  1. λ pipenv run pytest -k "test_one" -p no:warnings src/chapter-8/
  2. ============================ test session starts =============================
  3. platform win32 -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0
  4. rootdir: D:\Personal Files\Projects\pytest-chinese-doc\src\chapter-8, inifile: pytest.ini
  5. collected 2 items / 1 deselected / 1 selected
  6. src\chapter-8\test_show_warnings.py . [100%]
  7. ====================== 1 passed, 1 deselected in 0.03s =======================

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

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

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

5. DeprecationWarningPendingDeprecationWarning告警

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

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

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

注意:

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

  1. # _pytest/warnings.py
  2. if not sys.warnoptions:
  3. # if user is not explicitly configuring warning filters, show deprecation warnings by default (#2908)
  4. warnings.filterwarnings("always", category=DeprecationWarning)
  5. warnings.filterwarnings("always", category=PendingDeprecationWarning)

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

5.1. pytest.deprecated_call方法

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

  1. # src/chapter-8/test_deprecation.py
  2. import warnings
  3. import pytest
  4. def api_call_v1():
  5. warnings.warn('v1版本已废弃,请使用v2版本的api;', DeprecationWarning)
  6. return 200
  7. def test_deprecation():
  8. assert pytest.deprecated_call(api_call_v1) == 200

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

  1. def test_deprecation():
  2. with pytest.deprecated_call():
  3. assert api_call_v1() == 200

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

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

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

  1. # _pytest/warnings.py
  2. def deprecated_call(func=None, *args, **kwargs):
  3. __tracebackhide__ = True
  4. if func is not None:
  5. args = (func,) + args
  6. return warns((DeprecationWarning, PendingDeprecationWarning), *args, **kwargs)

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

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

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

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

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

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

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

    1. import re
    2. def test_deprecation():
    3. with pytest.warns((DeprecationWarning, PendingDeprecationWarning)) as records:
    4. assert api_call_v1() == 200
    5. assert len(records) == 1
    6. assert re.search(r'v1版本已废弃', records[0].message.args[0])

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

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

  1. # _pytest/recwarn.py
  2. def warns(
  3. expected_warning: Union["Type[Warning]", Tuple["Type[Warning]", ...]],
  4. *args: Any,
  5. match: Optional[Union[str, "Pattern"]] = None,
  6. **kwargs: Any
  7. ) -> Union["WarningsChecker", Any]:
  8. __tracebackhide__ = True
  9. if not args:
  10. if kwargs:
  11. msg = "Unexpected keyword arguments passed to pytest.warns: "
  12. msg += ", ".join(sorted(kwargs))
  13. msg += "\nUse context-manager form instead?"
  14. raise TypeError(msg)
  15. return WarningsChecker(expected_warning, match_expr=match)
  16. else:
  17. func = args[0]
  18. if not callable(func):
  19. raise TypeError(
  20. "{!r} object (type: {}) must be callable".format(func, type(func))
  21. )
  22. with WarningsChecker(expected_warning):
  23. return func(*args[1:], **kwargs)

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

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

  1. def test_deprecation():
  2. with pytest.warns(Warning) as records:
  3. rsp = api_call_v1()
  4. if not records:
  5. pytest.fail('期望 api_call_v1 触发一个告警,实际上没有;')
  6. assert rsp == 200

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

7. recwarn fixture

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

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

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

  1. import re
  2. def test_deprecation(recwarn):
  3. api_call_v1()
  4. assert len(recwarn) == 1
  5. w = recwarn.pop() # 不指定告警类型的话,默认弹出最先捕获的告警
  6. assert issubclass(w.category, (DeprecationWarning, PendingDeprecationWarning))
  7. 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 -- 捕获告警信息的更多相关文章

  1. Python中的Warnings模块忽略告警信息

    写了个小工具,其中涉及到从远程数据库中查询并返回,数据库是utf8编码,但是我的工具用的是GB2312编码,因此在返回数据的时候,有部分数据出现了:Truncated incorrect DECIMA ...

  2. PROCEDURE_监测系统_告警信息存储过程—产生告警信息插入告警表

    create or replace procedure proc_alarmlog(in_id   in number, --采集器编码                                 ...

  3. zabbix使用企业微信发送告警信息

    用qq邮箱发送告警信息一点都不方便,看到网上说也可以使用微信发送告警信息,所以就试了一下. 首先先试着在虚拟主机上给微信发送信息. 我们需要注册企业微信,注册时有一个地方需要注意,就是注册时选择组织, ...

  4. 一个简单好用的zabbix告警信息发送工具

    之前使用邮件和短信发送zabbix告警信息,但告警信息无法实时查看或者无法发送,故障无法及时通知运维人员. 后来使用第三方微信接口发送信息,愉快地用了一年多,突然收费了. zabbix告警一直是我的痛 ...

  5. [置顶] zabbix通过lykchat发送告警信息配置过程

    本文介绍zabbix通过lykchat发送告警信息配置过程. lykchat代码在https://github.com/lykops/lykchat/ 步骤 编写脚本 1).查看服务器端的配置文件et ...

  6. [置顶] 个人微信号发送zabbix告警信息

    之前使用邮件和短信发送zabbix告警信息,但告警信息无法实时查看或者无法发送,故障无法及时通知运维人员. 后来使用第三方微信接口发送信息,愉快地用了一年多,突然收费了. zabbix告警一直是我的痛 ...

  7. [置顶] 一个简单好用的zabbix告警信息发送工具

    之前使用邮件和短信发送zabbix告警信息,但告警信息无法实时查看或者无法发送,故障无法及时通知运维人员. 后来使用第三方微信接口发送信息,愉快地用了一年多,突然收费了. zabbix告警一直是我的痛 ...

  8. [置顶] zabbix告警信息-lykchat信息发送系统

    lykchat信息发送系统 lykchat信息发送系统是Python3开发的,通过模拟微信网页端,基于个人微信号,为系统管理人员提供信息发送工具. 实现的功能有用户登录管理.微信登陆管理和微信信息发送 ...

  9. Python 操作Zabbix API 获取ERROR级别告警信息并打印

    1.需求:有一个语音合成播报项目,要实时获取zabbix的ERROR级别以上告警信息,将该信息合成语音播报出去.(合成语音及播报已经完成) 2.现实:整理zabbix告警级别,将不太重要的告警放到ER ...

随机推荐

  1. 遇到不支持的 Oracle 数据类型 USERDEFINED

    以前都是sql查询mdb空间数据没有什么问题,今天在用sql方式查询Oracle中的空间数据时候,出现错误.它不支持geometry.空间数据都带有shape属性.只要不查询shape字段就没问题.但 ...

  2. Flume 学习笔记之 Flume NG+Kafka整合

    Flume NG集群+Kafka集群整合: 修改Flume配置文件(flume-kafka-server.conf),让Sink连上Kafka hadoop1: #set Agent name a1. ...

  3. yii2 对字段 自动加一 或 减一

    用于数字类型,将值进行 累加 或者 累减 $count = 1, 就是加一 .   $count = -1, 就是减一 . $effect =  Model::updateAllCounters( [ ...

  4. 最简单的JS实现json转csv

    工作久了,总会遇到各种各样的数据处理工作,比如同步数据,初始化一些数据,目前比较流行的交互数据格式就是JSON,可是服务器中得到的JSON数据如果提供给业务人员看的话可能会非常不方便,这时候,转成CS ...

  5. Spring Boot WebFlux 增删改查完整实战 demo

    03:WebFlux Web CRUD 实践 前言 上一篇基于功能性端点去创建一个简单服务,实现了 Hello .这一篇用 Spring Boot WebFlux 的注解控制层技术创建一个 CRUD ...

  6. .netCore+Vue 搭建的简捷开发框架 (4)--NetCore 基础 -2

    上节中,我们初步的介绍了一下NetCore的一些基础知识,为了控制篇幅(其实也是因为偷懒),我将NetCore 基础分为两部分来写. 0.WebAPI 项目的建立 1..NetCore 项目执行(加载 ...

  7. 关于人工智能和python

    人工智能的话题在近几年可谓是相当火热,前几天看快本时其中有一个环节就是关于人工智能的,智能家电.智能机器人.智能工具等等,在我的印象里,提到人工智能就会出现 Python,然后我便在网上查找了相关信息 ...

  8. 小白学 Python(1):开篇

    人生苦短,我用 Python 引言 大家好,可能大家都对我比较熟悉了,不熟悉请去面壁(现在熟悉一下也来得及)~ 简单做一个自我介绍,我是极客挖掘机的唯一作者,一位油腻的 Java 程序员[臭鸡蛋什么的 ...

  9. Jenkins节点配置

    1.系统管理---configure Global Security(全局安全设置)---Tcp port for inbound agents---指定端口---服务器防火墙中开放此端口 点击 ag ...

  10. MySQL 插入记录时自动更新时间戳

    将字段设置成timestamp类型,同时默认值设置成 CURRENT_TIMESTAMP.