python - pytest.mark.parametrize 测试用例的副本而不更改原始版本

标签 python python-3.x pytest parametrized-testing

背景

我们有一大套可重用的测试用例,可以在不同的环境中运行。对于我们带有测试用例的套件...

@pytest.mark.system_a
@pytest.mark.system_b
# ...
@pytest.mark.system_z
test_one_thing_for_current_system():
   assert stuff()

...我们在系统 A 上执行 pytest -m system_a,在系统 B 上执行 pytest -m system_b,依此类推。

目标

我们只想为一个系统参数化多个测试用例,但既不想复制粘贴这些测试用例,也基于命令行动态生成参数化参数pytest -m

我们的尝试

复制并标记

我们不是复制粘贴测试用例,而是将现有的函数对象分配给一个变量。对于...

class TestBase:
    @pytest.mark.system_a
    def test_reusable_thing(self):
        assert stuff()

class TestReuse:
    test_to_be_altered = pytest.mark.system_z(TestBase.test_reusable_thing)

... pytest --collect-only 显示两个测试用例

 TestBase.test_reusable_thing
 TestReuse.test_to_be_altered

但是,其中一种情况下的 pytest.mark 也会影响另一种情况。因此,这两种情况都被标记为 system_a system_z

在添加 mark 之前使用 copy.deepcopy(TestBase.test_reusable_thing) 并更改副本的 __name__ 没有帮助。

复制并参数化

上面的示例仅用于说明,因为它实际上并没有改变测试用例。对于我们的用例,我们尝试了类似...

class TestBase:
    @pytest.fixture
    def thing(self):
        return 1
    
    @pytest.mark.system_a
    # ...
    @pytest.mark.system_y
    def test_reusable_thing(self, thing, lots, of, other, fixtures):
        # lots of code
        assert stuff() == thing

copy_of_test = copy.deepcopy(TestBase.test_reusable_thing)
copy_of_test.__name__ = "test_altered"

class TestReuse:
    test_altered = pytest.mark.system_z(
        pytest.mark.parametrize("thing", [1, 2, 3])(copy_of_test)
    )

由于上述问题,这对所有系统的test_reusable_thing进行参数化,而我们只想对system_z的副本进行参数化。

问题

我们如何为 system_z 参数化 test_reusable_thing ...

  • 不更改 test_reusable_thing 的实现,
  • 并且无需更改固定装置thing的实现,
  • 并且无需复制粘贴 test_reusable_thing 的实现
  • 并且无需手动创建包装函数def test_altered,我们必须复制粘贴请求的固定装置,然后将它们传递给TestBase().test_reusable_thing(thing,lots,of,other) 、灯具)

pytest 必须以某种方式将副本链接到原始版本。如果我们知道如何(例如,基于像 __name__ 这样的变量),我们就可以断开链接。

最佳答案

您可以将参数化推迟到 pytest_generate_tests hookimpl。您可以使用它来添加自定义逻辑以隐式填充测试参数,例如

def pytest_generate_tests(metafunc):
    # test has `my_arg` in parameters
    if 'my_arg' in metafunc.fixturenames:
        marker_for_system_z = metafunc.definition.get_closest_marker('system_z')
        # test was marked with `@pytest.mark.system_z`
        if marker_for_system_z is not None:
            values_for_system_z = some_data.get('z')
            metafunc.parametrize('my_arg', values_for_system_z)

通过 system 参数将标记名称传递给 test_reusable_thing 的演示示例:

import pytest


def pytest_generate_tests(metafunc):
    if 'system' in metafunc.fixturenames:
        args = [marker.name for marker in metafunc.definition.iter_markers()]
        metafunc.parametrize('system', args)


class Tests:

    @pytest.fixture
    def thing(self):
        return 1

    @pytest.mark.system_a
    @pytest.mark.system_b
    @pytest.mark.system_c
    @pytest.mark.system_z
    def test_reusable_thing(self, thing, system):
        assert system.startswith('system_')

运行此命令将总共产生四个测试:

test_spam.py::Tests::test_reusable_thing[system_z] PASSED
test_spam.py::Tests::test_reusable_thing[system_c] PASSED
test_spam.py::Tests::test_reusable_thing[system_b] PASSED
test_spam.py::Tests::test_reusable_thing[system_a] PASSED

关于python - pytest.mark.parametrize 测试用例的副本而不更改原始版本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72292106/

相关文章:

python - 为什么这些图像 gridspec 子图的高度略有不同?

python - 错误:::ValueError: 无法将字符串转换为 float : '28,37'

python-3.x - pytest 和 Failed : Database access not allowed, 使用 "django_db"标记,或 "db"或 "transactional_db"固定装置来启用它

python - Pytest:获取所有测试的地址

python - 从 pip 转移到诗歌,现在 pytest-cov 不会收集覆盖率数据

python - 在 Ubuntu 上使用带有 SpatiaLite 的 GeoDjango 时出错

python - Pycharm manage.py 自动完成错误

python - requests.get 到 localhost 超时

Python venv 和 virtualenvwrapper 结合

python - 如何获取请求 'get' 来遵循所有重定向