pytest-捕获告警信息
作者:互联网
1.告警信息的默认捕获行为
pytest可以自动捕获测试中产生的告警信息,在测试结束后进行展示
import warnings
def api_v1():
warnings.warn(UserWarning('请使用新版本的api'))
return 1
def test_one():
assert api_v1() == 1
返回结果:
D:\pytest\exercise\chapter8>pytest -q test_show_warning.py
. [100%]
==================================================================== warnings summary ====================================================================
test_show_warning.py::test_one
D:\pytest\exercise\chapter8\test_show_warning.py:7: UserWarning: 请使用新版本的api
warnings.warn(UserWarning('请使用新版本的api'))
-- Docs: https://docs.pytest.org/en/stable/warnings.html
1 passed, 1 warning in 0.02s
可以通过-W arg、命令行选项来自定义告警的捕获:
arg参数的格式为:
action:message:catagory:module:lineno;
- action:只能在error、ignore、always(all)、default、module、once中取值,默认取值为default
- category必须是warning的子类,默认取值为Warning类,表示所有的告警
- module必须为字符串,表示特定模块产生的告警信息
一些常见的使用场景:
- 忽略某一种类型的告警信息,例如会略UserWarning类型的告警(-W ignore::UserWarning)
D:\pytest\exercise\chapter8>pytest -W ignore::UserWarning test_show_warning.py
================================================================= test session starts ==================================================================
platform win32 -- Python 3.8.5, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: D:\pytest\exercise\chapter8
plugins: allure-pytest-2.8.36
collected 1 item
test_show_warning.py . [100%]
================================================================== 1 passed in 0.07s ============================================================
- 将一种类型的告警转换为异常来处理,例如:将UserWaarning告警转换为异常处理(-W error::Userwarning)
D:\pytest\exercise\chapter8>pytest -W error::UserWarning test_show_warning.py
================================================================== test session starts ===================================================================
platform win32 -- Python 3.8.5, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: D:\pytest\exercise\chapter8
plugins: allure-pytest-2.8.36
collected 1 item
test_show_warning.py F [100%]
======================================================================== FAILURES ========================================================================
________________________________________________________________________ test_one ________________________________________________________________________
def test_one():
> assert api_v1() == 1
test_show_warning.py:11:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def api_v1():
> warnings.warn(UserWarning('请使用新版本的api'))
E UserWarning: 请使用新版本的api
test_show_warning.py:7: UserWarning
================================================================ short test summary info =================================================================
FAILED test_show_warning.py::test_one - UserWarning: 请使用新版本的api
=================================================================== 1 failed in 0.11s ====================================================================
- 只展示某一个模块中产生的告警,例如:只展示test_show_warnings模块中产生的告警信息,忽略其他的告警(-W ignore -W default:::test_show_warnings)
D:\pytest\exercise>pytest -W ignore -W default:::test_show_warnings chapter8/
================================================================= test session starts ==================================================================
platform win32 -- Python 3.8.5, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: D:\pytest\exercise
plugins: allure-pytest-2.8.36
collected 1 item
chapter8\test_show_warning.py . [100%]
================================================================== 1 passed in 0.03s ===================================================================
这里我们演示了多个-W选项的组合操作,优先级是从左到右依次递增的;这里如果将它们调换一下顺序(即-W default:::test_show_warnings -W ignore),因为-W ignore最后生效,覆盖掉之前的操作,最终的结果就是我们一个告警信息都没有捕获到;
- 也可以通过在pytest.ini文件中配置filterwarnings选项,来实现同样的操作。
[pytest]
filterwarnings =
ignore
default:::test_show_warnings
2. @pytest.mark.filterwarnings
上述操作是通过命令行执行的,使用起来多有不便,如何能够在用例、类、模块级别上自定义告警的捕获行为?可通过为测试项添加告警过滤器实现。
pytest.ini中定义的配置,禁止了除了test_show_warning模块外,其他的所有告警捕获行为。
现在新增用例, 禁止捕获由它所触发的用户告警:
@pytest.mark.filterwarnings('ignore::UserWarning')
def test_two():
assert api_v1() ==1
测试结果:
D:\pytest\exercise\chapter8>pytest -k "test_two"
================================================================= test session starts ==================================================================
platform win32 -- Python 3.8.5, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: D:\pytest\exercise\chapter8, configfile: pytest.ini
plugins: allure-pytest-2.8.36
collected 2 items / 1 deselected / 1 selected
test_show_warning.py . [100%]
=========================================================== 1 passed, 1 deselected in 0.05s ============================================================
测试test_one:
D:\pytest\exercise\chapter8>pytest -k "test_one"
================================================================= test session starts ==================================================================
platform win32 -- Python 3.8.5, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: D:\pytest\exercise\chapter8, configfile: pytest.ini
plugins: allure-pytest-2.8.36
collected 2 items / 1 deselected / 1 selected
test_show_warning.py . [100%]
=========================================================== 1 passed, 1 deselected in 0.03s ============================================================
我们可以通过将@pytest.mark.filterwarnings应用于测试类为类中每一个测试用例添加过滤器。也可以通过设置pytestmark变量为整个测试模块中所有的用例添加告警过滤器:
pytestmark = pytest.mark.filterwarnings("error")
3. 取消告警信息的展示
D:\pytest\exercise\chapter8>pytest -k "test_one" --disable-warnings
================================================================= test session starts ==================================================================
platform win32 -- Python 3.8.5, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: D:\pytest\exercise\chapter8, configfile: pytest.ini
plugins: allure-pytest-2.8.36
collected 2 items / 1 deselected / 1 selected
test_show_warning.py . [100%]
=========================================================== 1 passed, 1 deselected in 0.03s ============================================================
4. 不使用告警的捕获功能
上小节中只是不展示捕获到的告警信息,我们可以通过- p no::warnings命令禁止告警的捕获行为。
D:\pytest\exercise\chapter8>pytest -k "test_one" -p no:warnings test_show_warning.py
================================================================= test session starts ==================================================================
platform win32 -- Python 3.8.5, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: D:\pytest\exercise\chapter8, configfile: pytest.ini
plugins: allure-pytest-2.8.36
collected 2 items / 1 deselected / 1 selected
test_show_warning.py . [100%]
=========================================================== 1 passed, 1 deselected in 0.02s ============================================================
5. DeprecationWarning和PendingDeprecationWarning告警
pytest会默认捕获DeprecationWarning和PendingDeprecationWarning类型的告警。有时候,你并不需要这种行为,可以通过在pytest.ini添加配置;例如,忽略告警信息匹配".*U.*mode is deprecated"的DeprecationWarning告警:
[pytest]
filterwarnings =
ignore:.*U.*mode is deprecated:DeprecationWarning
5.1 pytest.deprecated_call方法
可以通过deprecated_call方法确保一段代码触发DeprecationWarning或者PendingDeprecationWarning告警。
import warnings
import pytest
def api_call_v1():
warnings.warn("v1版本已经弃用,请使用v2版本的api")
def test_deprecation():
assert pytest.deprecated_call(api_call_v1()) == 200
返回结果:
D:\pytest\exercise\chapter8>pytest test_deprecation.py
================================================================= test session starts ==================================================================
platform win32 -- Python 3.8.5, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: D:\pytest\exercise\chapter8, configfile: pytest.ini
plugins: allure-pytest-2.8.36
collected 1 item
test_deprecation.py F [100%]
======================================================================= FAILURES =======================================================================
___________________________________________________________________ test_deprecation ___________________________________________________________________
def test_deprecation():
> assert pytest.deprecated_call(api_call_v1()) == 200
E assert WarningsChecker(record=True) == 200
E + where WarningsChecker(record=True) = <function deprecated_call at 0x0000018F59D06280>(None)
E + where <function deprecated_call at 0x0000018F59D06280> = pytest.deprecated_call
E + and None = api_call_v1()
test_deprecation.py:12: AssertionError
=============================================================== short test summary info ================================================================
FAILED test_deprecation.py::test_deprecation - assert WarningsChecker(record=True) == 200
================================================================== 1 failed in 0.09s ===================================================================
deprecated_call也支持上下文管理器的写法:
def test_deprecation2():
with pytest.deprecated_call():
assert api_call_v1() == 200
6. 编写触发期望告警的断言
可以使用pytest.warns()作为上下文管理器,编写一个触发期望告警的断言,和pytest.raises()的用法很接近。
deprecaed_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_deprecation3():
with pytest.warns((DeprecationWarning,PendingDeprecationWarning),match=r'v1版本已废弃'):
assert api_call_v1() == 200
测试结果:
D:\pytest\exercise\chapter8>pytest -k test_deprecation3 test_deprecation.py
================================================================= test session starts ==================================================================
platform win32 -- Python 3.8.5, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: D:\pytest\exercise\chapter8, configfile: pytest.ini
plugins: allure-pytest-2.8.36
collected 3 items / 2 deselected / 1 selected
test_deprecation.py F [100%]
======================================================================= FAILURES =======================================================================
__________________________________________________________________ test_deprecation3 ___________________________________________________________________
def test_deprecation3():
with pytest.warns((DeprecationWarning,PendingDeprecationWarning),match=r'v1版本已废弃'):
> assert api_call_v1() == 200
E assert None == 200
E + where None = api_call_v1()
test_deprecation.py:20: AssertionError
=============================================================== short test summary info ================================================================
FAILED test_deprecation.py::test_deprecation3 - assert None == 200
=========================================================== 1 failed, 2 deselected in 0.12s ============================================================
- 我们也可以直接传递可低啊用对象,表达式返回执行这个可调用对象的结果:
def test_deprecation4():
assert pytest.warns((DeprecationWarning,PendingDeprecationWarning), api_call_v1,
match=r'和 pytest.raises() 方法一样,这时 pytest 不再判断告警信息是否正确') == 200
测试结果:
D:\pytest\exercise\chapter8>pytest -k test_deprecation4 test_deprecation.py
================================================================== test session starts ===================================================================
platform win32 -- Python 3.8.5, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: D:\pytest\exercise\chapter8, configfile: pytest.ini
plugins: allure-pytest-2.8.36
collected 4 items / 3 deselected / 1 selected
test_deprecation.py F [100%]
======================================================================== FAILURES ========================================================================
___________________________________________________________________ test_deprecation4 ____________________________________________________________________
def test_deprecation4():
> assert pytest.warns((DeprecationWarning,PendingDeprecationWarning), api_call_v1,
match=r'和 pytest.raises() 方法一样,这时 pytest 不再判断告警信息是否正确') == 200
E Failed: DID NOT WARN. No warnings of type (<class 'DeprecationWarning'>, <class 'PendingDeprecationWarning'>) was emitted. The list of emitted warn
ings is: [UserWarning('v1版本已经弃用,请使用v2版本的api')].
test_deprecation.py:24: Failed
================================================================ short test summary info =================================================================
FAILED test_deprecation.py::test_deprecation4 - Failed: DID NOT WARN. No warnings of type (<class 'DeprecationWarning'>, <class 'PendingDeprecationWarni...
============================================================ 1 failed, 3 deselected in 0.10s =============================================================
- pytest.warns()可以返回一个列表,包含所有捕获到的告警对象 (warnings.WarningMessage)
import re
def test_deprecation5():
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])
返回结果:
D:\pytest\exercise\chapter8>pytest -k test_deprecation5 test_deprecation.py
================================================================= test session starts ==================================================================
platform win32 -- Python 3.8.5, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: D:\pytest\exercise\chapter8, configfile: pytest.ini
plugins: allure-pytest-2.8.36
collected 5 items / 4 deselected / 1 selected
test_deprecation.py F [100%]
======================================================================= FAILURES =======================================================================
__________________________________________________________________ test_deprecation5 ___________________________________________________________________
def test_deprecation5():
with pytest.warns((DeprecationWarning,PendingDeprecationWarning)) as records:
> assert api_call_v1() == 200
E assert None == 200
E + where None = api_call_v1()
test_deprecation.py:31: AssertionError
=============================================================== short test summary info ================================================================
FAILED test_deprecation.py::test_deprecation5 - assert None == 200
=========================================================== 1 failed, 4 deselected in 0.11s ============================================================
实际上返回的不是一个列表。只是实现了__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.2 自定义失败时的提示消息
def test_deprecation6():
with pytest.warns(Warning) as records:
rsp = api_call_v1()
if not records:
pytest.fail('期望apu_call_v1 触发告警,实际上没有')
assert rsp == 200
测试结果:
D:\pytest\exercise\chapter8>pytest -k test_deprecation6 test_deprecation.py
================================================================= test session starts ==================================================================
platform win32 -- Python 3.8.5, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: D:\pytest\exercise\chapter8, configfile: pytest.ini
plugins: allure-pytest-2.8.36
collected 6 items / 5 deselected / 1 selected
test_deprecation.py F [100%]
======================================================================= FAILURES =======================================================================
__________________________________________________________________ test_deprecation6 ___________________________________________________________________
def test_deprecation6():
with pytest.warns(Warning) as records:
rsp = api_call_v1()
if not records:
pytest.fail('期望apu_call_v1 触发告警,实际上没有')
> assert rsp == 200
E assert None == 200
test_deprecation.py:41: AssertionError
=============================================================== short test summary info ================================================================
FAILED test_deprecation.py::test_deprecation6 - assert None == 200
=========================================================== 1 failed, 5 deselected in 0.09s ============================================================
7. recwarn fixture
recwarn是一个用例级别的fixture,可以用于记录用例产生的所有告警。
def test_reprecation7(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])
测试结果:
D:\pytest\exercise\chapter8>pytest -k test_deprecation7 test_deprecation.py
================================================================= test session starts ==================================================================
platform win32 -- Python 3.8.5, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: D:\pytest\exercise\chapter8, configfile: pytest.ini
plugins: allure-pytest-2.8.36
collected 7 items / 7 deselected
================================================================ 7 deselected in 0.03s =================================================================
pytest自定义的告警类型
pytest自定义的告警类型:
标签:chapter8,api,捕获,py,告警信息,v1,pytest,test 来源: https://blog.csdn.net/lemonquan/article/details/117258885