pytest8.x版本 中文使用文档-------28.示例:使用自定义标记

目录

为测试函数添加标记并选择它们进行运行

基于节点ID选择测试

使用 -k expr 来根据测试名称选择测试

注册标记

标记整个类或模块

在使用 parametrize 时标记个别测试

自定义标记和命令行选项以控制测试运行

将可调用对象传递给自定义标记

读取从多个位置设置的标记(或标记器)

使用 pytest 标记特定平台的测试

基于测试名称自动添加标记(Markers)


这里有一些使用“如何为测试函数添加属性标记”机制的例子。

为测试函数添加标记并选择它们进行运行

你可以像这样为测试函数添加自定义元数据“标记”:

# test_server.py 文件的内容  
  
import pytest  
  
  
@pytest.mark.webtest  
def test_send_http():  
    pass  # 为你的应用执行一些Web测试  
  
  
def test_something_quick():  
    pass  
  
  
def test_another():  
    pass  
  
  
class TestClass:  
    def test_method(self):  
        pass

然后,你可以限制测试运行,仅运行被标记为 webtest 的测试:

$ pytest -v -m webtest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: .pytest_cache
rootdir: /home/sweet/project
collecting ... collected 4 items / 3 deselected / 1 selected

test_server.py::test_send_http PASSED                                [100%]

===================== 1 passed, 3 deselected in 0.12s ======================

或者,相反地,运行除了 webtest 标记外的所有测试:

$ pytest -v -m "not webtest"
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: .pytest_cache
rootdir: /home/sweet/project
collecting ... collected 4 items / 1 deselected / 3 selected

test_server.py::test_something_quick PASSED                          [ 33%]
test_server.py::test_another PASSED                                  [ 66%]
test_server.py::TestClass::test_method PASSED                        [100%]

===================== 3 passed, 1 deselected in 0.12s ======================

基于节点ID选择测试

你可以提供一个或多个节点ID作为位置参数,以仅选择指定的测试。这使得基于模块、类、方法或函数名称选择测试变得容易。

$ pytest -v test_server.py::TestClass::test_method
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: .pytest_cache
rootdir: /home/sweet/project
collecting ... collected 1 item

test_server.py::TestClass::test_method PASSED                        [100%]

============================ 1 passed in 0.12s =============================

您还可以在类上选择:

$ pytest -v test_server.py::TestClass
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: .pytest_cache
rootdir: /home/sweet/project
collecting ... collected 1 item

test_server.py::TestClass::test_method PASSED                        [100%]

============================ 1 passed in 0.12s =============================

或选择多个节点:

$ pytest -v test_server.py::TestClass test_server.py::test_send_http
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: .pytest_cache
rootdir: /home/sweet/project
collecting ... collected 2 items

test_server.py::TestClass::test_method PASSED                        [ 50%]
test_server.py::test_send_http PASSED                                [100%]

============================ 2 passed in 0.12s =============================

注意

节点ID的形式为module.py::class::methodmodule.py::function。节点ID控制哪些测试被收集,因此module.py::class会选择该类的所有测试方法。对于参数化的fixture或测试,也会为每个参数创建一个节点,因此选择参数化测试时必须包含参数值,例如module.py::function[param]

当使用pytest命令的-rf选项运行时,失败的测试的节点ID会显示在测试摘要信息中。你也可以通过pytest --collect-only的输出结果来构造节点ID。这个命令会列出所有可收集的测试项及其节点ID,但不会执行它们。

使用 -k expr 来根据测试名称选择测试

在版本2.0/2.3.4中增加的功能:

您可以使用-k命令行选项来指定一个表达式,该表达式在测试名称上实现子字符串匹配,而不是像-m提供的那样对标记进行精确匹配。这使得基于测试名称选择测试变得非常容易:

在版本5.4中有所更改。

表达式匹配现在是不区分大小写的:

$ pytest -v -k http  # running with the above defined example module
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: .pytest_cache
rootdir: /home/sweet/project
collecting ... collected 4 items / 3 deselected / 1 selected

test_server.py::test_send_http PASSED                                [100%]

===================== 1 passed, 3 deselected in 0.12s ======================

注意:

  • 这里的 -k http 选项表示选择所有测试名称中包含“http”(不区分大小写)的测试。

你还可以运行除了匹配关键字以外的所有测试:

$ pytest -k "not send_http" -v
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: .pytest_cache
rootdir: /home/sweet/project
collecting ... collected 4 items / 1 deselected / 3 selected

test_server.py::test_something_quick PASSED                          [ 33%]
test_server.py::test_another PASSED                                  [ 66%]
test_server.py::TestClass::test_method PASSED                        [100%]

===================== 3 passed, 1 deselected in 0.12s ======================

或者,要选择包含“http”或“quick”的测试:

$ pytest -k "http or quick" -v
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python
cachedir: .pytest_cache
rootdir: /home/sweet/project
collecting ... collected 4 items / 2 deselected / 2 selected

test_server.py::test_send_http PASSED                                [ 50%]
test_server.py::test_something_quick PASSED                          [100%]

===================== 2 passed, 2 deselected in 0.12s ======================

你可以使用 and、or、not 以及括号来组合条件。

除了测试的名称外,-k 选项还会匹配测试的父级名称(通常是包含它的文件名和类名)、在测试函数上设置的属性、应用到测试或其父级上的标记,以及显式添加到测试或其父级上的任何额外关键字extra keywords

注册标记

为你的测试套件注册标记非常简单:

# content of pytest.ini
[pytest]
markers =
    webtest: mark a test as a webtest.
    slow: mark test as slow.

如上例所示,可以通过在各自的行中定义每个标记来注册多个自定义标记。每个标记后面跟着的冒号和简短描述用于说明该标记的用途。这样,在编写测试时,你就可以使用这些自定义标记来分类和组织你的测试了。

你可以查询你的测试套件中存在哪些标记——列表中包括了我们刚刚定义的 webtest 和 slow 标记,以及其他内置标记:

$ pytest --markers
@pytest.mark.webtest: mark a test as a webtest.

@pytest.mark.slow: mark test as slow.

@pytest.mark.filterwarnings(warning): add a warning filter to the given test. see https://docs.pytest.org/en/stable/how-to/capture-warnings.html#pytest-mark-filterwarnings

@pytest.mark.skip(reason=None): skip the given test function with an optional reason. Example: skip(reason="no way of currently testing this") skips the test.

@pytest.mark.skipif(condition, ..., *, reason=...): skip the given test function if any of the conditions evaluate to True. Example: skipif(sys.platform == 'win32') skips the test if we are on the win32 platform. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-skipif

@pytest.mark.xfail(condition, ..., *, reason=..., run=True, raises=None, strict=xfail_strict): mark the test function as an expected failure if any of the conditions evaluate to True. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-xfail

@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see https://docs.pytest.org/en/stable/how-to/parametrize.html for more info and examples.

@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see https://docs.pytest.org/en/stable/explanation/fixtures.html#usefixtures

@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible. DEPRECATED, use @pytest.hookimpl(tryfirst=True) instead.

@pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible. DEPRECATED, use @pytest.hookimpl(trylast=True) instead.

关于如何从插件添加和使用标记的示例,请参见“自定义标记和命令行选项以控制测试运行” Custom marker and command line option to control test runs

注意

建议明确注册标记,以便:

  1. 在你的测试套件中有一个定义标记的地方。
  2. 通过 pytest --markers 查询现有标记时能得到清晰的输出。
  3. 如果你使用了 --strict-markers 选项,那么函数标记中的拼写错误将被视为错误。

标记整个类或模块

你可以使用 pytest.mark 装饰器与类结合,以将标记应用于其所有测试方法:

# test_mark_classlevel.py 文件的内容  
import pytest  
  
  
@pytest.mark.webtest  
class TestClass:  
    def test_startup(self):  
        pass  
  
    def test_startup_and_more(self):  
        pass

这相当于直接将装饰器应用于这两个测试函数。即,TestClass 类中的所有测试方法(test_startup 和 test_startup_and_more)都将被标记为 webtest。这样,你就可以通过 pytest -m webtest 命令来运行所有被标记为 webtest 的测试,包括这个类中的所有测试方法。

在模块级别应用标记,可以使用 pytestmark 全局变量:

import pytest  
pytestmark = pytest.mark.webtest

或者应用多个标记:

pytestmark = [pytest.mark.webtest, pytest.mark.slowtest]

由于历史原因,在类装饰器引入之前,也可以像这样在测试类上设置pytestmark 属性:

import pytest  
  
class TestClass:  
    pytestmark = pytest.mark.webtest

在使用 parametrize 时标记个别测试

在使用 parametrize 时,应用一个标记会使它应用到每个单独的测试上。但是,也可以将标记应用到单个测试实例上:

import pytest  
  
@pytest.mark.foo  
@pytest.mark.parametrize(  
    ("n", "expected"),  
    [(1, 2), pytest.param(1, 3, marks=pytest.mark.bar), (2, 3)]  
)  
def test_increment(n, expected):  
    assert n + 1 == expected

在这个例子中,标记“foo”将应用到三个测试中的每一个上,而“bar”标记仅应用于第二个测试。同样地,skip 和 xfail 标记也可以以这种方式应用,参见Skip/xfail with parametrize.。这里的关键是 pytest.param 函数的使用,它允许你为参数化列表中的特定参数对指定额外的参数,包括 marks。通过给 pytest.param 传递 marks=pytest.mark.some_mark,你可以为该参数对生成的测试实例添加特定的标记。这种方式提供了对参数化测试的高度灵活性,允许你针对不同的测试实例应用不同的测试行为或配置。

自定义标记和命令行选项以控制测试运行

插件可以提供自定义标记并根据这些标记实现特定行为。以下是一个完整的示例,它添加了一个命令行选项和一个参数化测试函数标记,用于通过命名环境来运行指定的测试:

# content of conftest.py
import pytest  
  
def pytest_addoption(parser):  
    """  
    添加命令行选项  
    """  
    parser.addoption(  
        "-E",  
        action="store",  
        metavar="NAME",  
        help="只运行与指定环境名称 NAME 相匹配的测试。",  
    )  
  
def pytest_configure(config):  
    """  
    配置阶段注册额外的标记  
    """  
    # 注册一个额外的标记  
    config.addinivalue_line(  
        "markers", "env(name): 标记测试以仅在指定的命名环境下运行"  
    )  
  
def pytest_runtest_setup(item):  
    """  
    在测试运行之前设置阶段,根据环境标记跳过不匹配的测试  
    """  
    envnames = [mark.args[0] for mark in item.iter_markers(name="env")]  
    if envnames:  
        # 如果测试函数被标记了 env 并且没有匹配到命令行指定的环境  
        if item.config.getoption("-E") not in envnames:  
            pytest.skip(f"测试需要的环境在 {envnames!r} 中")

pytest_addoption 函数向 pytest 添加了一个新的命令行选项 -E,它允许用户指定一个环境名称。pytest_configure 函数则通过 config.addinivalue_line 方法注册了一个名为 env 的自定义标记,该标记接受一个参数(即环境名称),用于标记测试函数以指示它们应该仅在特定环境下运行。最后,pytest_runtest_setup 函数在测试准备阶段被调用,它检查测试项(即测试函数)是否使用了 env 标记,并且如果使用了,它会检查通过 -E 命令行选项指定的环境名称是否与测试项中指定的环境名称之一相匹配。如果不匹配,则使用 pytest.skip 函数跳过该测试项,并显示一条消息说明测试需要的环境。

使用此本地插件的测试文件示例:

# content of test_someenv.py
import pytest  
  
@pytest.mark.env("stage1")  
def test_basic_db_operation():  
    pass

以及一个指定了与测试所需不同的环境的示例调用:

$ pytest -E stage2
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 1 item

test_someenv.py s                                                    [100%]

============================ 1 skipped in 0.12s ============================

由于测试 test_basic_db_operation 被标记为仅在 stage1 环境下运行,但命令行选项 -E 指定了 stage2,因此该测试被跳过了。

下面是另一个指定了测试所需的确切环境的示例调用:

$ pytest -E stage1  
=========================== test session starts ============================  
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y  
rootdir: /home/sweet/project  
collected 1 item  
  
test_someenv.py .                                                     [100%]  
  
============================ 1 passed in 0.12s =============================

--markers 选项总是给你提供一个可用的标记(markers)列表:

$ pytest --markers
@pytest.mark.env(name): mark test to run only on named environment

@pytest.mark.filterwarnings(warning): add a warning filter to the given test. see https://docs.pytest.org/en/stable/how-to/capture-warnings.html#pytest-mark-filterwarnings

@pytest.mark.skip(reason=None): skip the given test function with an optional reason. Example: skip(reason="no way of currently testing this") skips the test.

@pytest.mark.skipif(condition, ..., *, reason=...): skip the given test function if any of the conditions evaluate to True. Example: skipif(sys.platform == 'win32') skips the test if we are on the win32 platform. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-skipif

@pytest.mark.xfail(condition, ..., *, reason=..., run=True, raises=None, strict=xfail_strict): mark the test function as an expected failure if any of the conditions evaluate to True. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-xfail

@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see https://docs.pytest.org/en/stable/how-to/parametrize.html for more info and examples.

@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see https://docs.pytest.org/en/stable/explanation/fixtures.html#usefixtures

@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible. DEPRECATED, use @pytest.hookimpl(tryfirst=True) instead.

@pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible. DEPRECATED, use @pytest.hookimpl(trylast=True) instead.

将可调用对象传递给自定义标记

下面是将在后续示例中使用的配置文件内容:

import sys  
  
def pytest_runtest_setup(item):  
    # 遍历名为 'my_marker' 的所有标记  
    for marker in item.iter_markers(name="my_marker"):  
        # 打印出这个标记对象  
        print(marker)  
        # 刷新标准输出,确保立即看到打印的内容  
        sys.stdout.flush()

我们定义了一个 pytest 钩子(hook)pytest_runtest_setup。这个钩子在测试运行设置阶段被调用,对于每个测试项(item)都会执行一次。我们在这个钩子内部使用 item.iter_markers(name="my_marker") 来遍历所有名为 my_marker 的标记。对于找到的每个标记,我们都将其打印出来,并立即刷新标准输出流,以确保即使测试运行缓慢或输出被缓冲,我们也能立即看到这些标记。

自定义标记可以设置其参数,即 args 和 kwargs 属性,这些属性可以通过将其作为可调用对象调用或使用 pytest.mark.MARKER_NAME.with_args 来定义。这两种方法大多数时候能达到相同的效果。

然而,如果有一个可调用对象作为唯一的位置参数且没有关键字参数,那么使用 pytest.mark.MARKER_NAME(c) 并不会将 c 作为位置参数传递,而是会用自定义标记来装饰 c(参见  MarkDecorator)。幸运的是,pytest.mark.MARKER_NAME.with_args 提供了解决方案:

# content of test_custom_marker.py
import pytest  
  
# 定义一个可调用对象,这里是一个简单的函数  
def hello_world(*args, **kwargs):  
    return "Hello World"  
  
# 使用 pytest.mark.my_marker.with_args 来将 hello_world 函数作为参数传递给 my_marker 标记  
# 注意:这里假设 my_marker 已经被定义为接受参数的自定义标记,但这通常需要通过 pytest 插件或复杂的装饰器逻辑来实现  
# 对于简单的演示,我们假设这个行为是存在的  
@pytest.mark.my_marker.with_args(hello_world)  
def test_with_args():  
    pass

输出如下:

$ pytest -q -s  
Mark(name='my_marker', args=(<function hello_world at 0xdeadbeef0001>,), kwargs={})  
.  
1 passed in 0.12s

我们可以看到,自定义标记的参数集被扩展了,包含了 hello_world 函数。这是将自定义标记作为可调用对象(它在幕后调用 __call__ 方法)与使用 with_args 方法之间的关键区别。

读取从多个位置设置的标记(或标记器)

如果你在你的测试套件中大量使用标记(或标记器),你可能会遇到这样的情况:一个测试函数被多次应用了同一个标记。从插件代码中,你可以读取所有这些设置。以下是一个例子:

# test_mark_three_times.py 文件的内容  
import pytest  
  
# 在模块级别应用标记  
pytestmark = pytest.mark.glob("module", x=1)  
  
# 定义一个测试类,并在类级别应用标记  
@pytest.mark.glob("class", x=2)  
class TestClass:  
    # 在测试函数级别再次应用标记  
    @pytest.mark.glob("function", x=3)  
    def test_something(self):  
        pass

在这里,我们有一个名为“glob”的标记被三次应用于同一个测试函数。从conftest.py文件中,我们可以像这样读取它:

# conftest.py 文件的内容  
import sys  
  
def pytest_runtest_setup(item):  
    # 遍历测试项上所有名为"glob"的标记  
    for mark in item.iter_markers(name="glob"):  
        # 打印出每个标记的参数和关键字参数  
        print(f"glob args={mark.args} kwargs={mark.kwargs}")  
        # 刷新标准输出流,确保立即打印结果  
        sys.stdout.flush()

pytest_runtest_setup 是一个 pytest 钩子函数,它在每个测试项的设置阶段被调用。我们利用 item.iter_markers(name="glob") 来遍历测试项上所有名为“glob”的标记。对于每个找到的标记,我们打印出它的位置参数(args)和关键字参数(kwargs)。

如果我们不捕获输出并运行这个测试,我们会看到以下结果:

$ pytest -q -s  
glob args=('function',) kwargs={'x': 3}  
glob args=('class',) kwargs={'x': 2}  
glob args=('module',) kwargs={'x': 1}  
.  
1 passed in 0.12s

使用 pytest 标记特定平台的测试

考虑你有一个测试套件,它使用特定的平台标记来标记测试,比如 pytest.mark.darwinpytest.mark.win32 等,同时你也有一些在所有平台上都能运行且没有特定标记的测试。现在,如果你想要一种方式来仅运行特定于你当前平台的测试,你可以使用以下插件代码:

# conftest.py 文件的内容  
  
import sys  
  
import pytest  
  
# 定义一个包含所有已知平台名称的集合  
ALL = set("darwin linux win32".split())  
  
def pytest_runtest_setup(item):  
    # 获取测试项上所有平台标记的名称,并与所有已知平台名称求交集   
    supported_platforms = ALL.intersection(mark.name for mark in item.iter_markers() if hasattr(mark, 'name'))  
      
    # 获取当前运行测试的操作系统平台  
    plat = sys.platform  
      
    # 如果测试项有特定的平台标记,并且当前平台不在这些标记中,则跳过该测试  
    if supported_platforms and plat.lower() not in supported_platforms:  
        pytest.skip(f"cannot run on platform {plat}")  
  

intersection() 是 Python 中的一个方法,通常用于集合(set)类型的数据。它返回两个或多个集合的交集,如果集合之间没有共同元素,则返回一个空集合。在上例pytest_runtest_setup 函数中,intersection() 方法被用于计算一个由测试项上所有平台标记名称组成的集合(通过遍历 item.iter_markers() 并提取 mark.name)与包含所有已知平台名称的集合 ALL 之间的交集。

如果测试被指定为针对不同的平台,那么这些测试将被跳过。让我们编写一个小的测试文件来展示这是如何工作的:

# test_plat.py 文件的内容  
  
import pytest  
  
@pytest.mark.darwin  
def test_if_apple_is_evil():  
    pass  
  
@pytest.mark.linux  
def test_if_linux_works():  
    pass  
  
@pytest.mark.win32  
def test_if_win32_crashes():  
    pass  
  
def test_runs_everywhere():  
    pass

然后,你会看到两个测试被跳过,另外两个测试按预期执行:

$ pytest -rs # this option reports skip reasons
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 4 items

test_plat.py s.s.                                                    [100%]

========================= short test summary info ==========================
SKIPPED [2] conftest.py:13: cannot run on platform linux
======================= 2 passed, 2 skipped in 0.12s =======================

请注意,如果你通过标记命令行选项来指定一个平台,像这样:

$ pytest -m linux
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 4 items / 3 deselected / 1 selected

test_plat.py .                                                       [100%]

===================== 1 passed, 3 deselected in 0.12s ======================

那么,未标记的测试将不会被执行。因此,这是一种将运行限制在特定测试上的方法。

基于测试名称自动添加标记(Markers)

如果你有一个测试套件,其中的测试函数名称指示了测试的类型,你可以实现一个钩子(hook),该钩子可以自动定义标记(markers),这样你就可以使用 -m 选项来指定运行哪些测试了。让我们看看下面的测试模块:

# test_module.py 文件的内容  
  
def test_interface_simple():  
    assert 0  
  
def test_interface_complex():  
    assert 0  
  
def test_event_simple():  
    assert 0  
  
def test_something_else():  
    assert 0

我们希望在 conftest.py 插件中动态定义两个标记,并可以做到这一点:

# conftest.py 文件的内容  
  
import pytest  
  
def pytest_collection_modifyitems(items):  
    for item in items:  
        if "interface" in item.nodeid:  
            item.add_marker(pytest.mark.interface)  
        elif "event" in item.nodeid:  
            item.add_marker(pytest.mark.event)

定义了一个 pytest_collection_modifyitems 钩子函数,该函数会在 pytest 收集完所有测试项之后被调用。通过遍历这些测试项(items),我们可以检查每个测试项的唯一标识符(item.nodeid),该标识符通常包含了测试文件的路径和测试函数的名称。如果测试项的唯一标识符中包含 "interface" 字符串,我们就使用 item.add_marker 方法为该测试项添加一个名为 "interface" 的标记。类似地,如果唯一标识符中包含 "event" 字符串,我们就添加一个名为 "event" 的标记。

现在我们可以使用 -m 选项来选择一组测试:

$ pytest -m interface --tb=short  
=========================== test session starts ============================  
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y  
rootdir: /home/sweet/project  
collected 4 items / 2 deselected / 2 selected  
  
test_module.py FF                                                    [100%]  
  
================================= FAILURES =================================  
__________________________ test_interface_simple ___________________________  
test_module.py:4: in test_interface_simple  
    assert 0  
E   assert 0  
__________________________ test_interface_complex __________________________  
test_module.py:8: in test_interface_complex  
    assert 0  
E   assert 0  
========================= short test summary info ==========================  
FAILED test_module.py::test_interface_simple - assert 0  
FAILED test_module.py::test_interface_complex - assert 0  
===================== 2 failed, 2 deselected in 0.12s ======================

pytest -m interface --tb=short 告诉 pytest 只运行那些被标记为 "interface" 的测试,并使用简短的回溯(traceback)来显示失败信息。

或者同时选择“event”和 “interface”测试:

$ pytest -m "interface or event" --tb=short
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 4 items / 1 deselected / 3 selected

test_module.py FFF                                                   [100%]

================================= FAILURES =================================
__________________________ test_interface_simple ___________________________
test_module.py:4: in test_interface_simple
    assert 0
E   assert 0
__________________________ test_interface_complex __________________________
test_module.py:8: in test_interface_complex
    assert 0
E   assert 0
____________________________ test_event_simple _____________________________
test_module.py:12: in test_event_simple
    assert 0
E   assert 0
========================= short test summary info ==========================
FAILED test_module.py::test_interface_simple - assert 0
FAILED test_module.py::test_interface_complex - assert 0
FAILED test_module.py::test_event_simple - assert 0
===================== 3 failed, 1 deselected in 0.12s ======================

  • 12
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值