【问题标题】:Pytest marks: mark entire directory / packagepytest 标记:标记整个目录/包
【发布时间】:2019-07-14 21:49:30
【问题描述】:

我正在使用 Pytest 测试一个组件的多个版本。有些测试可以在所有版本上运行,有些是特定于版本的。例如

tests
|
|-- version1_tests
|   |-- test_feature_1_1.py
|   |-- test_feature_1_2.py
|   |-- test_feature_1_n.py
| 
|-- version2_tests
|   |-- test_feature_2_1.py
|   |-- test_feature_2_2.py
|   |-- test_feature_2_n.py
|
|-- common_tests
|   |-- test_feature_common_1.py
|   |-- test_feature_common_2.py
|   |-- test_feature_common_n.py

我想标记我的测试,以便我可以选择是要从命令行测试版本 1 (version1_tests + common_tests) 还是版本 2 (version2_tests + common_tests)。

我目前这样做的方式是为每个测试模块添加一个 pytest 标记,然后从命令行指定标记。例如在test_feature_1_1.py:

import pytest
pytestmark = pytest.mark.version1

class TestSpecificFeature(object):
    ...

然后运行:python -m pytest -m "common and version1"

这很好用,但我必须手动将标记添加到每个模块,这很乏味,因为实际上有几十个(而不是示例中的 3 个)。

我们曾经使用 Robot Framework,通过在 __init__.robot 文件中添加标签来“标记”整个文件夹是微不足道的。在 Pytest 中是否有任何等效的方法可以做到这一点,或者是我能做到的最好的标记每个模块?

【问题讨论】:

  • pytest -k "common and version1" 不行吗?除此之外,您当然可以根据您需要的任何条件通过item.add_marker() 动态分配标记。
  • 虽然它不像我最初要求的那样使用标记,但就我的情况而言,它工作得很好,我认为这是一个比我正在寻找的更好的解决方案。您是否介意将其发布为答案,并且可能还详细说明使用 item.add_marker() 或任何其他可能实际使用标记的解决方案?这样一来,如果有人稍后阅读本文并且使用表达式的解决方案对他们不起作用,他们可以有另一种选择。

标签: python testing pytest


【解决方案1】:

您可以使用item.add_marker() 方法在运行时将标记注册到收集的测试。这是pytest_collection_modifyitems中注册标记的示例:

import pathlib
import pytest


def pytest_collection_modifyitems(config, items):
    # python 3.4/3.5 compat: rootdir = pathlib.Path(str(config.rootdir))
    rootdir = pathlib.Path(config.rootdir)
    for item in items:
        rel_path = pathlib.Path(item.fspath).relative_to(rootdir)
        mark_name = next((part for part in rel_path.parts if part.endswith('_tests')), '').removesuffix('_tests')
        if mark_name:
            mark = getattr(pytest.mark, mark_name)
            item.add_marker(mark)

将代码写入项目根目录下的conftest.py,试试看:

$ pytest -m "common or version2" --collect-only -q
tests/common_tests/test_feature_common_1.py::test_spam
tests/common_tests/test_feature_common_1.py::test_eggs
tests/common_tests/test_feature_common_2.py::test_spam
tests/common_tests/test_feature_common_2.py::test_eggs
tests/common_tests/test_feature_common_n.py::test_spam
tests/common_tests/test_feature_common_n.py::test_eggs
tests/version2_tests/test_feature_2_1.py::test_spam
tests/version2_tests/test_feature_2_1.py::test_eggs
tests/version2_tests/test_feature_2_2.py::test_spam
tests/version2_tests/test_feature_2_2.py::test_eggs
tests/version2_tests/test_feature_2_n.py::test_spam
tests/version2_tests/test_feature_2_n.py::test_eggs

只选择了common_testsversion2_tests 下的测试。

说明

对于每个收集到的测试项目,我们提取相对于项目根目录(rel_path)的路径,rel_path_tests 结尾的第一部分将用作标记名称提取的源。例如。 collect_tests 是标记名称collect 等的来源。一旦我们有了标记名称,我们就创建标记(使用getattr,因为我们不能使用属性访问)并通过item.add_marker(mark) 附加标记。您可以编写自己的、不那么抽象的版本,例如

for item in items:
    if `common_tests` in str(item.fspath):
        item.add_marker(pytest.mark.common)
    elif `version1_tests` in str(item.fspath):
        item.add_marker(pytest.mark.version1)
    elif `version2_tests` in str(item.fspath):
        item.add_marker(pytest.mark.version2)

注册标记

使用最新版本的pytest,您应该会收到PytestUnknownMarkWarning,因为动态生成的标记未注册。查看Registering markers 部分的解决方案 - 您可以在pytest.ini 中添加标记名称:

[pytest]
markers =
    common
    version1
    version2

或通过pytest_configure钩子动态添加它们,例如

def pytest_configure(config):
    rootdir = pathlib.Path(config.rootdir)
    for dir_ in rootdir.rglob('*_tests'):
        mark_name = dir_.stem.removesuffix('_tests')
        config.addinivalue_line('markers', mark_name)

【讨论】:

  • rstrip 在这里没有正确使用。如果目录名是e2e_tests,而您执行rstrip('_tests'),您将得到e2,因为它正在搜索_tests 中的任何要删除的字符,直到找到2,这不是在列表中。 'e2e_tests'.rstrip('_tests') == 'e2' 试试:mark_name = dir_.stem[:-len('_tests')]
【解决方案2】:

让我在这里建议一种不同的方法。
最好只有一种方法来运行测试。

您必须让您的测试能够区分当前正在测试的版本。

这样你就只能拥有一个common tests 文件夹。

如果无法在特定版本上运行测试,请使用skipif
如果在特定版本上测试失败,请使用xfail

请参考the skipping docs

【讨论】:

    【解决方案3】:

    您可以通过将__init__.py 文件放在带有版本标签的每个目录中来做完全相同的事情:

    pytestmark = pytest.mark.version1
    

    并在测试中导入它

    from . import pytestmark
    

    【讨论】:

      猜你喜欢
      • 2020-12-13
      • 1970-01-01
      • 2013-11-26
      • 1970-01-01
      • 2010-12-12
      • 1970-01-01
      • 1970-01-01
      • 2021-11-30
      • 2012-07-08
      相关资源
      最近更新 更多