可以通过命令行查看所有 marker,包括内置和自定义的
pytest --markers
内置 marker 本文先讲 usefixtures 、filterwarnings 、skip 、skipif 、xfail 这 5 个。参数化的 marker 我会写在《pytest 参数化》中,hook 的 marker 我会写在《pytest hook》中,插件的 marker(pytest-ordering、allure 等)我会写在《pytest 插件》中。当前只需知道有以上这些分类的 marker 即可。
如果我们只想把 fixture 注入到 test 中,test 不直接访问 fixture 的时候,就需要用到 usefixtures。
示例,test 需要一个临时目录,但是并不需要知道这个目录具体路径在哪
# content of conftest.py
import os
import shutil
import tempfile
import pytest
@pytest.fixture
def cleandir():
old_cwd = os.getcwd()
newpath = tempfile.mkdtemp()
os.chdir(newpath)
yield
os.chdir(old_cwd)
shutil.rmtree(newpath)
# content of test_setenv.py
import os
import pytest
@user2res("cleandir")
class TestDirectoryInit:
def test_cwd_starts_empty(self):
assert os.listdir(os.getcwd()) == []
with open("myfile", "w") as f:
f.write("hello")
def test_cwd_again_starts_empty(self):
assert os.listdir(os.getcwd()) == []
TestDirectoryInit 的测试方法需要一个临时目录作为当前工作目录,在类上添加@pytest.mark.usefixtures("cleandir")
,类的方法不加 fixture 也能有"cleandir"的效果。
usefixtures 可以添加多个 fixture
@user4res("cleandir", "anotherfixture")
usefixtures 可以用在pytestmark,作用域是定义所在 module 的所有 tests
pytestmark = pytest.mark.usefixtures("cleandir")
usefixtures 也可以用在pytest.ini,作用域是整个项目的所有 tests
# content of pytest.ini
[pytest]
usefixtures = cleandir
不过需要注意的是 fixture 函数本身是不能用 usefixtures 的,如果想要嵌套 fixture,只能通过在 fixture 修饰的函数中,添加参数这种方式。
过滤警告信息。
示例,api_v1() 抛出了 “api v1” 的警告,test_one() 函数使用 filterwarnings 过滤掉了
import warnings
def api_v1():
warnings.warn(UserWarning("api v1, should use functions from v2"))
return 1
@user5rnings("ignore:api v1")
def test_one():
assert api_v1() == 1
同样可以添加到 pytestmark 和 pytest.ini 中。
跳过,不测试。
示例,skip 需要添加 reason 哦
@pytest.mark.skip(reason="no way of currently testing this")
def test_the_unknown():
...
不过,更实用的方式是调用 pytest.skip(reason) 函数,而不是用 mark,这样就可以用 if 判断跳不跳
def test_function():
if not valid_config():
pytest.skip("unsupported configuration")
allow_module_level 可以跳过整个 module
import sys
import pytest
if not sys.platform.startswith("win"):
pytest.skip("skipping windows-only tests", allow_module_level=True)
if 判断跳不跳,还可以用 skipif。
示例,如果 Python 版本小于 3.6 就跳过测试
import sys
@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher")
def test_function():
...
如果想在 summary 中看到 reason,需要添加-rs 参数。
可以把 skipif 赋值给变量,然后直接引用变量,或者把变量 import 到其他 module 中使用
# content of test_mymodule.py
import mymodule
minversion = pytest.mark.skipif(
mymodule.__versioninfo__ < (1, 1), reason="at least mymodule-1.1 required"
)
@minversion
def test_function():
...
# test_myothermodule.py
from test_mymodule import minversion
@minversion
def test_anotherfunction():
...
skipif 添加到 class 上,会跳过类中所有方法。
可以使用 pytestmark 跳过 module 内所有 test
# test_module.py
pytestmark = pytest.mark.skipif(...)
如果 function 有多个 skipif 作用,只要有一个为 True,就会跳过。
明知失败,依然前行!不好意思跑偏了。xfail 就是 expected fail,预期失败
@pytest.mark.xfail
def test_function():
...
执行后 summary 不会统计为"failed",会单独列出来。如果结果失败了,“expected to fail” (XFAIL);如果结果成功了,“unexpectedly passing” (XPASS)。但是整个执行结果是” Tests passed“。
if 判断
def test_function():
if not valid_config():
pytest.xfail("failing configuration (but should work)")
值得注意的是,marker 会继续执行所有 test 代码,pytest.xfail() 函数会抛出异常,中断执行后续代码
添加condition,判断条件
@pytest.mark.xfail(sys.platform == "win32", reason="bug in a 3rd party library")
def test_function():
...
添加reason,理由
@pytest.mark.xfail(reason="known parser issue")
def test_function():
...
添加raises,抛出异常/错误
@pytest.mark.xfail(raises=RuntimeError)
def test_function():
...
添加run,禁止运行
@pytest.mark.xfail(run=False)
def test_function():
...
添加strict,严格模式,即使 xpass 也会强制失败,summary 中有输出信息”[XPASS(strict)] “,测试结果为” Tests failed“。
@pytest.mark.xfail(strict=True)
def test_function():
...
断言成功也强制失败,确实够强势的!
可以在 ini 文件中定义全局 strict
[pytest]
xfail_strict=true
在命令行添加--runxfail,忽略 xfail marker,相当于没有添加这个标记的效果,该成功就成功,该失败就失败,再强势也不虚,哈哈,恶灵退散。
pytest --runxfail
pytest --runxfail
再强势也不虚,恶灵退散,哈哈。
通过注解自定义 marker
# content of test_server.py
import pytest
@pytest.mark.webtest
def test_send_http():
pass # perform some webtest test for your app
def test_something_quick():
pass
def test_another():
pass
class TestClass:
def test_method(self):
pass
在命令行通过-m
指定运行 mark 打标的 test
$ pytest -v -m webtest
也可以反选
$ pytest -v -m "not webtest"
但是,这样定义的 marker 是未注册的!在执行后会警告,PytestUnknownMarkWarning。如果添加了命令行参数--strict-markers
,未注册的 marker 会报错。
可以在 pytest.ini 文件中注册,冒号后面的所有代码都是 marker 说明,包括换行
[pytest]
markers =
slow: marks tests as slow (deselect with '-m "not slow"')
serial
更高级的,可以在 pytest_configure hook 函数中注册,这主要用在第三方插件
def pytest_configure(config):
config.addinivalue_line(
"markers", "env(name): mark test to run only on named environment"
)
本文介绍了 5 个 pytest 内置的 marker,接着介绍了如何自定义 marker 和注册 marker。通过 marker,可以让我们更灵活的执行用例。
参考资料
docs-pytest-org-en-stable
如果你觉得这篇文章写的还不错的话,关注公众号 “测试老树”,你的支持就是我写文章的最大动力。