根据配置选项参数化会话夹具



我正在使用pytest,并在 conftest.py 中具有类似以下内容的内容:

def pytest_addoption(parser):
parser.addoption('--foo', required=True, help='Foo name.')

@pytest.fixture(scope='session')
def foo(pytestconfig):
with Foo(pytestconfig.getoption('foo')) as foo_obj:
yield foo_obj

我想将--foo选项更改为

parser.addoption('--foo', action='append', help='Foo names.')

并具有一个单独的Foo对象,其中为每个提供的名称生成会话范围。 通常,我会使用pytest_generate_tests以这种方式对灯具进行参数化。 那是

def pytest_generate_tests(metafunc):
if 'foo' in metafunc.fixturenames:
metafunc.parametrize('foo', map(Foo, metafunc.config.getoption('foo')))

但是,如果我正确理解pytest_generate_tests的工作原理,将为每个测试函数单独创建Foo对象,从而破坏会话装置的全部意义。

似乎解决此问题的最简单方法是使夹具间接化。也就是说,您将配置值传递给灯具,但随后让它管理自己的设置。这样 pytest 将遵循灯具的范围设置。例如:

def pytest_generate_tests(metafunc):
# assuming --foo has been set as a possible parameter for pytest
if "foo" in metafunc.fixturenames and metafunc.config.option.foo is not None:       
metafunc.parametrize("foo", metafunc.config.option.foo, indirect=True)
@pytest.fixture(scope='session')
def foo(request):
if not hasattr(request, 'param'):
pytest.skip('no --foo option set')
elif isinstance(request.param, str):
return Foo(request.param)
else:
raise ValueError("invalid internal test config")

总的来说,这看起来像:

conftest.py

def pytest_addoption(parser):
parser.addoption('--foo', action='append', help='Foo value')

test_something.py

import pytest
# keep track of how many Foos have been instantiated
FOO_COUNTER = 0
class Foo:
def __init__(self, value):
global FOO_COUNTER
self.value = value
self.count = FOO_COUNTER
FOO_COUNTER += 1
print(f'creating {self} number {self.count}')
def __repr__(self):
return f'Foo({self.value})'
def pytest_generate_tests(metafunc):
if "foo" in metafunc.fixturenames and metafunc.config.option.foo is not None:       
metafunc.parametrize("foo", metafunc.config.option.foo, indirect=True)
@pytest.fixture(scope='session')
def foo(request):
if not hasattr(request, 'param'):
pytest.skip('no --foo option set')
elif isinstance(request.param, str):
return Foo(request.param)
else:
raise ValueError("invalid internal test config")
def test_bar(foo):
assert isinstance(foo, Foo)
assert foo.value in list('abx')
def test_baz(foo):
assert isinstance(foo, Foo)
assert foo.value in list('aby')
# test name is to encourage pytest to run this test last (because of side 
# effects of Foo class has on FOO_COUNTER)
def test_zzz_last():
# only passes when exactly two foo options set
assert FOO_COUNTER == 2

如果正好使用两个--foo选项运行,则test_zzz_last通过。一个或三个--foo选项,此测试失败。这表明每个--foo选项只创建了一个Foo实例,并且每个实例在测试之间共享。零--foo选项将导致任何需要跳过foo夹具的测试。

如果一个--foo选项被赋予一个不是abxy的值,那么test_bartest_baz都将失败。因此,我们可以看到我们的配置选项正在进入foo夹具。

最新更新