背景
我们有一大套可重用的测试用例,可以在不同的环境中运行。对于我们带有测试用例的套件...
@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/