Pytest:如何使用从夹具返回的列表参数化测试?

win*_*rrr 12 python pytest

我想使用由夹具动态创建的列表来参数化测试,如下所示:

@pytest.fixture
def my_list_returning_fixture(depends_on_other_fixtures):
    return ["a dynamically created list which", depends_on_other_fixtures]
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?或者,我如何确保首先调用某个夹具- 这将在它发生之前解决这个问题。


我已经尝试过的

  • 我试图用夹具参数化测试(这只会导致错误,因为 python 认为我想交出函数本身):

    @pytest.mark.parametrize(
        "an_element_from_the_list_of_my_fixture", 
        my_list_returning_fixture
    )
    def test_the_list(an_element_from_the_list_of_my_fixture):
        print(an_element_from_the_list_of_my_fixture)
    
    Run Code Online (Sandbox Code Playgroud)

    像普通函数一样调用夹具my_list_returning_fixture()也会导致错误!Python 不知道如何填充夹具的“参数”(实际上只是其他夹具)并显示关于传递的参数太少的错误消息......

    因此我需要 pytest 来注入depends_on_other_fixtures依赖项,所以我不能像普通函数一样调用它。

  • 我还尝试在列表夹具和测试之间插入另一个夹具,如下所示:

    @pytest.fixture(params=my_list_returning_fixture)
    def my_interposed_parametrized_fixture(request):
        return request.param
    
    def test_the_list(my_interposed_parametrized_fixture):
        ...
    
    Run Code Online (Sandbox Code Playgroud)
  • 我也尝试使用间接参数化,但它也不起作用......


使用静态列表很容易

我知道可以很容易地使用给定的参数对测试进行参数化,如下list所示:

the_list = [1, 2, 3]
@pytest.mark.parametrize("an_element_from_the_list", the_list)
def test_the_list(an_element_from_the_list):
    print(an_element_from_the_list)
Run Code Online (Sandbox Code Playgroud)

这将导致三个测试。列表中的每个元素一个。

Mad*_*ist 5

简短的回答是你不能按照你想要的方式来做,即通过装置:https : //github.com/pytest-dev/pytest/issues/2155。基本上,pytest 必须预先知道产生或返回的事物的数量,以便正确计算夹具和测试依赖关系图。

似乎唯一的方法是在将列表元素传递给任何 pytests 的装饰器之前修复它们。这是一个与您的其他问题相关的示例,表明该问题无法通过生成器解决:

import pytest

def gen_lines():
    with open('file_that_does_not_exist') as f:
        yield from f

@pytest.fixture(scope='session')
def create_file():
    with open('file_that_does_not_exist', 'w') as f:
        print('ABC', file=f)
        print('DEF', file=f)

@pytest.fixture(params=gen_lines())
def line_fixture(request):
    return request.param

def test_line(line_fixture):
    assert True
Run Code Online (Sandbox Code Playgroud)

当 pytest 将您的生成器转换为列表时,这将在收集时失败。出于同样的原因,添加对line_fixtureon的依赖create_file也无济于事。

此时您唯一真正的选择是create_file在模块加载时或之前运行。

import pytest

def gen_lines():
    with open('file_that_does_not_exist') as f:
        yield from f

def create_file():
    with open('file_that_does_not_exist', 'w') as f:
        print('ABC', file=f)
        print('DEF', file=f)

create_file()

@pytest.fixture(params=gen_lines())
def line_fixture(request):
    return request.param

def test_line(line_fixture):
    assert True
Run Code Online (Sandbox Code Playgroud)

该列表本身不必是静态的。它不能由夹具创建。但不要让它阻止你。您可以将用于定义和运行的代码create_file放在一个单独的模块中,然后将其作为实用程序导入到任何需要的地方。这将掩盖所有杂乱的细节,并使您的代码看起来像使用设备一样干净。

  • 您还可以考虑更新 pytest。虽然很高兴知道您预先进行了多少测试,但我不确定是否有充分的理由不支持至少惰性生成器作为参数。 (2认同)