本文总结pytest提供的mark功能。
mark可分为2类:
系统内置mark可通过pytest --markers
指令查看。
如下:有一些笔者没弄懂,请了解的读者解惑。
'''
@pytest.mark.flaky(reruns=1, reruns_delay=0):将test标记为重新运行“reruns”的值代表的次数。在重新运行之间添加'reruns_delay'秒的延迟。
@pytest.mark.allure_label: allure label marker(没搞懂)
@pytest.mark.allure_link: allure link marker(没搞懂)
@pytest.mark.allure_display_name: allure test name marker(没搞懂)
@pytest.mark.allure_description: allure description(没搞懂)
@pytest.mark.allure_description_html: allure description html(没搞懂)
@pytest.mark.filterwarnings(warning): 向给定的测试添加一个警告过滤器。参考https://docs.pytest.org/en/latest/warnings.html pytest-mark-filterwarnings(没有模拟出来,warning应该被限制只能传入某个警告的对象名)
@pytest.mark.skip(reason=None):使用可选的原因跳过给定的测试函数。例如:skip(reason="no way of current testing this")跳过测试。
@pytest.mark.skipif(condition,reason):如果eval(条件)结果为真值,则跳过给定的测试函数。评估发生在模块全局上下文中。例如:skipif(“系统。如果我们在win32平台上,平台== "win32"')将跳过测试。参见https://docs.pytest.org/en/latest/skipping.html
@pytest.mark.xfail(condition, reason=None, run=True, raises =None, strict=False):如果eval(condition)的值为真,则将测试函数标记为预期失败。可选地指定更好报告的理由,如果您甚至不想执行测试函数,则run=False。如果只期望特定的异常,您可以在引发中列出它们,如果测试以其他方式失败,则将报告为真正的失败。参见https://docs.pytest.org/en/latest/skipping.html
@pytest.mark.parametrize(argnames, argvalues):调用一个测试函数多次,依次传递不同的参数。如果argnames只指定一个名称,则argvalues通常需要一个值列表;如果argnames指定多个名称,则需要一个值元组列表。例如:@ parametertrize ('arg1',[1,2])将导致对修饰测试函数的两个调用,一个调用arg1=1,另一个调用arg1=2。有关更多信息和示例,请参见https://docs.pytest.org/en/latest/parametertrize.html。
@pytest.mark.usefixtures(fixturename1, fixturename2, ...):将测试标记为会使用所有指定的fixture。参考https://docs.pytest.org/en/latest/fixture.html usefixtures
@pytest.mark.tryfirst:标记一个钩子实现函数,这样插件机制就会尽可能早地调用它。(没搞懂)
@pytest.mark.trylast:标记一个钩子实现函数,使插件机制尽可能晚地调用它。(没搞懂)
'''
依次通过示例总结以上常用的内置mark:
使用flaky标记,指示如下用例失败会重新运行2次,重新运行的事件间隔为0s。
# ./test_case/test_func.py
import pytest
from func import *
class TestFunc:
def test_add_by_class(self):
assert add(2,3) == 5
@pytest.mark.flaky(reruns = 2)
def test_add_by_func_aaa():
a = 4
b = 6
assert add(a,b) == 9
# ./run_test.py
import pytest
if __name__ == '__main__':
pytest.main(['-v'])
'''
stdout:
============================= test session starts =============================
platform win32 -- Python 3.7.0, pytest-5.3.4, py-1.8.1, pluggy-0.13.1 -- D:\Python3.7\python.exe
cachedir: .pytest_cache
rootdir: D:\Python3.7\project\pytest
plugins: allure-pytest-2.8.9, rerunfailures-8.0
collecting ... collected 2 items
test_case/test_func.py::TestFunc::test_add_by_class PASSED [ 50%]
test_case/test_func.py::test_add_by_func_aaa RERUN [100%]
test_case/test_func.py::test_add_by_func_aaa RERUN [100%]
test_case/test_func.py::test_add_by_func_aaa FAILED [100%]
================================== FAILURES ===================================
____________________________ test_add_by_func_aaa _____________________________
@pytest.mark.flaky(reruns = 2)
def test_add_by_func_aaa():
a = 4
b = 6
> assert add(a,b) == 9
E assert 10 == 9
E -10
E +9
test_case\test_func.py:14: AssertionError
==================== 1 failed, 1 passed, 2 rerun in 0.12s =====================
[Finished in 1.5s]
'''
使用可选的原因跳过给定的测试函数。例如:skip(reason=“no way of current testing this”)跳过测试。
# ./test_case/test_func.py
import pytest
from func import *
class TestFunc:
@pytest.mark.skip()
def test_add_by_class(self):
assert add(2,3) == 5
@pytest.mark.skip(reason="No No No")
def test_add_by_func_aaa():
a = 4
b = 6
assert add(a,b) == 9
# ./run_test.py
import pytest
if __name__ == '__main__':
pytest.main(['-v'])
'''
============================= test session starts =============================
platform win32 -- Python 3.7.0, pytest-5.3.4, py-1.8.1, pluggy-0.13.1 -- D:\Python3.7\python.exe
cachedir: .pytest_cache
rootdir: D:\Python3.7\project\pytest
plugins: allure-pytest-2.8.9, rerunfailures-8.0
collecting ... collected 2 items
test_case/test_func.py::TestFunc::test_add_by_class SKIPPED [ 50%]
test_case/test_func.py::test_add_by_func_aaa SKIPPED [100%]
============================= 2 skipped in 0.04s ==============================
[Finished in 1.4s]
'''
不知道把原因说明显示到哪里去了。
如果eval(条件)结果为真值,则跳过给定的测试函数。评估发生在模块全局上下文中。例如:skipif(“系统。如果我们在win32平台上,平台== “win32”’)将跳过测试。参见https://docs.pytest.org/en/latest/skipping.html
# ./test_case/test_func.py
import pytest
from func import *
class TestFunc:
@pytest.mark.skipif(1==2,reason="No No No")
def test_add_by_class(self):
assert add(2,3) == 5
@pytest.mark.skipif(1==1,reason="Y Y Y")
def test_add_by_func_aaa():
a = 4
b = 6
assert add(a,b) == 10
# ./run_test.py
import pytest
if __name__ == '__main__':
pytest.main(['-v'])
'''
============================= test session starts =============================
platform win32 -- Python 3.7.0, pytest-5.3.4, py-1.8.1, pluggy-0.13.1 -- D:\Python3.7\python.exe
cachedir: .pytest_cache
rootdir: D:\Python3.7\project\pytest
plugins: allure-pytest-2.8.9, rerunfailures-8.0
collecting ... collected 2 items
test_case/test_func.py::TestFunc::test_add_by_class PASSED [ 50%]
test_case/test_func.py::test_add_by_func_aaa SKIPPED [100%]
======================== 1 passed, 1 skipped in 0.04s =========================
[Finished in 1.4s]
'''
这个标记还必须指定reason,不然会报错。但是也没搞懂reason显示到哪里去了。
如果eval(condition)的值为真,则将测试函数标记为预期失败。可选地指定更好报告的理由,如果您甚至不想执行测试函数,则run=False。如果只期望特定的异常,您可以在引发中列出它们,如果测试以其他方式失败,则将报告为真正的失败。参见https://docs.pytest.org/en/latest/skipping.html
先说明该标记的几个参数,经过验证,得出如下结论:
xfail示例:
# ./test_case/test_func.py
import pytest
from func import *
class TestFunc:
def test_add_by_class(self):
assert add(2,3) == 5
@pytest.mark.xfail()
def test_add_by_func_aaa():
a = 4
b = 6
assert add(a,b) == 9
# ./run_test.py
import pytest
if __name__ == '__main__':
pytest.main(['-v'])
'''
============================= test session starts =============================
platform win32 -- Python 3.7.0, pytest-5.3.4, py-1.8.1, pluggy-0.13.1 -- D:\Python3.7\python.exe
cachedir: .pytest_cache
rootdir: D:\Python3.7\project\pytest
plugins: allure-pytest-2.8.9, rerunfailures-8.0
collecting ... collected 2 items
test_case/test_func.py::TestFunc::test_add_by_class PASSED [ 50%]
test_case/test_func.py::test_add_by_func_aaa XFAIL [100%]
======================== 1 passed, 1 xfailed in 0.10s =========================
[Finished in 1.5s]
'''
xpass示例:
# ./test_case/test_func.py
import pytest
from func import *
class TestFunc:
def test_add_by_class(self):
assert add(2,3) == 5
@pytest.mark.xfail()
def test_add_by_func_aaa():
a = 4
b = 6
assert add(a,b) == 10
# ./run_test.py
import pytest
if __name__ == '__main__':
pytest.main(['-v'])
'''
============================= test session starts =============================
platform win32 -- Python 3.7.0, pytest-5.3.4, py-1.8.1, pluggy-0.13.1 -- D:\Python3.7\python.exe
cachedir: .pytest_cache
rootdir: D:\Python3.7\project\pytest
plugins: allure-pytest-2.8.9, rerunfailures-8.0
collecting ... collected 2 items
test_case/test_func.py::TestFunc::test_add_by_class PASSED [ 50%]
test_case/test_func.py::test_add_by_func_aaa XPASS [100%]
======================== 1 passed, 1 xpassed in 0.05s =========================
[Finished in 1.4s]
'''
strict=True示例:
# ./test_case/test_func.py
import pytest
from func import *
class TestFunc:
def test_add_by_class(self):
assert add(2,3) == 5
@pytest.mark.xfail(strict=True)
def test_add_by_func_aaa():
a = 4
b = 6
assert add(a,b) == 10
# ./run_test.py
import pytest
if __name__ == '__main__':
pytest.main(['-v'])
'''
============================= test session starts =============================
platform win32 -- Python 3.7.0, pytest-5.3.4, py-1.8.1, pluggy-0.13.1 -- D:\Python3.7\python.exe
cachedir: .pytest_cache
rootdir: D:\Python3.7\project\pytest
plugins: allure-pytest-2.8.9, rerunfailures-8.0
collecting ... collected 2 items
test_case/test_func.py::TestFunc::test_add_by_class PASSED [ 50%]
test_case/test_func.py::test_add_by_func_aaa FAILED [100%]
================================== FAILURES ===================================
____________________________ test_add_by_func_aaa _____________________________
[XPASS(strict)]
========================= 1 failed, 1 passed in 0.04s =========================
[Finished in 1.4s]
'''
Tips:还可以通过pytest.ini配置文件达到strict=True的效果。这样的效果范围将是全局的。
[pytest]
xfail_strict = true
调用一个测试函数多次,依次传递不同的参数。如果argnames只指定一个名称,则argvalues通常需要一个值列表;如果argnames指定多个名称,则需要一个值元组列表。例如:@ parametertrize (‘arg1’,[1,2])将导致对修饰测试函数的两个调用,一个调用arg1=1,另一个调用arg1=2。有关更多信息和示例,请参见https://docs.pytest.org/en/latest/parametertrize.html。
测试用例的参数化标记,该标记将单独在另外的文章中总结
将测试标记为会使用所有指定的fixture。参考https://docs.pytest.org/en/latest/fixture.html usefixtures
pytest中指定测试用例使用fixture方法的标记。
指定测试用例使用fixture方法还可以通过像给函数传参的方式一般使用:
用法示例如下:
def test_case_001(fixturename1, fixturename2, ...):
# 打印ixturename1代表的fixture方法的返回值
print(ixturename1)
指定pytest运行时,需要指定-m选项,来使用自定义标记。
该类mark主要用于给测试用例分门别类,使得运行测试时可以指定运行符合哪一类标记的测试用例。官方说法是将测试用例标记并分组,以便快速选中并运行。
-m选项可以使用表达式指定多个标记名。
比如:
选中同时符合mark1和mark2标记的用例:“-m mark1 and mark2”, 如果一个用例只有mark1标记,没有mark2标记,则是不会被选中。
仅选中符合mark1但不符mark2标记的用例:“-m mark1 and not mark2”,如果一个用例只有mark1标记,没有mark2标记,则会被选中。
mark1或者mark2中有一个符合就选中的用例:“-m mark1 or mark2”。
示例:
# ./test_case/test_func.py
import pytest
from func import *
class TestFunc:
@pytest.mark.first
def test_add_by_class(self):
assert add(2,3) == 5
@pytest.mark.second
def test_add_by_func_aaa():
a = 4
b = 6
assert add(a,b) == 10
# ./run_test.py
# ./run_test.py
import pytest
if __name__ == '__main__':
# 该参数下:预期只会运行first标记的test_add_by_class
pytest.main(['-v','-m', 'first and not second'])
'''
============================= test session starts =============================
platform win32 -- Python 3.7.0, pytest-5.3.4, py-1.8.1, pluggy-0.13.1 -- D:\Python3.7\python.exe
cachedir: .pytest_cache
rootdir: D:\Python3.7\project\pytest
plugins: allure-pytest-2.8.9, rerunfailures-8.0
collecting ... collected 2 items / 1 deselected / 1 selected
test_case/test_func.py::TestFunc::test_add_by_class PASSED [100%]
============================== warnings summary ===============================
D:\Python3.7\lib\site-packages\_pytest\mark\structures.py:327
D:\Python3.7\lib\site-packages\_pytest\mark\structures.py:327: PytestUnknownMarkWarning: Unknown pytest.mark.first - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/latest/mark.html
PytestUnknownMarkWarning,
D:\Python3.7\lib\site-packages\_pytest\mark\structures.py:327
D:\Python3.7\lib\site-packages\_pytest\mark\structures.py:327: PytestUnknownMarkWarning: Unknown pytest.mark.second - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/latest/mark.html
PytestUnknownMarkWarning,
-- Docs: https://docs.pytest.org/en/latest/warnings.html
================= 1 passed, 1 deselected, 2 warnings in 0.04s =================
[Finished in 1.4s]
'''
这里有些警告,是因为我们的自定义标记没有注册,pytest识别为不合法标记。
如何注册自定义标记
通过在项目根目录下创建pytest.ini文件,然后添加section\option\value
如
[pytest]
markers=
markname1:description1
markname2:description2
addopts = --strict #该配置可以防止mark的拼写错误