目录html
pytest 3.1
版本新增特性python
pytest
能够自动捕获测试中产生的告警信息,并在执行结束后进行展现;git
下面这个例子,咱们在测试中人为的产生一条告警:github
# 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
;shell
action
只能在"error", "ignore", "always(all)", "default", "module", "once"
中取值,默认取值为default
;category
必须是Warning
的子类,默认取值为Warning
类,表示全部的告警;module
必须为字符串,表示特定模块产生的告警信息;下面是一些常见的使用场景:api
忽略某一种类型的告警信息;例如,忽略UserWarning
类型的告警(-W ignore::UserWarning
):session
λ 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
):ide
λ 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
@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")
咱们能够经过--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 =================
上一章咱们只是不展现捕获到的告警信息,这里咱们能够经过-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 =======================
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
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
咱们可使用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)
当咱们使用一段代码,指望其触发告警时,咱们能够经过一下方法,自定义失败时的提示消息,增长其可读性:
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
中自定义的提示消息;
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
的实例;
pytest
自定义的告警类型pytest
自己封装了一些告警的类型,并做为公共接口以供用户使用;
下面列举了一些常见的内部告警:
告警 | 父类 | 描述 |
---|---|---|
PytestWarning |
UserWarning |
全部告警的父类; |
PytestCollectionWarning |
PytestWarning |
不可以收集某个模块中的用例; |
PytestConfigWarning |
PytestWarning |
配置错误; |
PytestUnknownMarkWarning |
PytestWarning |
使用了未知的标记; |
更多的内部告警能够查看:https://docs.pytest.org/en/5.1.3/warnings.html#pytest.PytestWarning