【问题标题】:Pytest: How to parametrize a test with a list that is returned from a fixture?Pytest:如何使用从夹具返回的列表对测试进行参数化?
【发布时间】:2019-07-03 21:20:44
【问题描述】:

我想用一个由夹具动态创建的列表对测试进行参数化,如下所示:

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

我怎样才能做到这一点?或者,我怎样才能确保a certain fixture gets called first - 这甚至可以在它发生之前解决这个问题。


我已经尝试过的

  • 我尝试使用夹具对测试进行参数化(这只会导致错误,因为 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)
    

    my_list_returning_fixture() 这样的普通函数调用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):
        ...
    
  • 我也尝试过使用间接参数化,但也没有用...


使用静态列表很容易

我知道使用给定的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)

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

【问题讨论】:

  • 与您的其他问题相同的问题 - 在fixture/mark.parametrize 装饰器中评估夹具/测试参数时,尚未执行任何夹具,并且内部缓存中没有夹具结果。如果夹具没有其他依赖项,直接的解决方法是将夹具简单地调用为普通函数,例如@pytest.mark.parametrize('arg', fixturefunc()),但那不是你的情况。
  • 顺便说一句,有一个旧的open issue 用于将固定装置作为参数传递给mark.parametrize
  • @hoefling。而且它看起来不会很快得到解决:github.com/pytest-dev/pytest/issues/2155

标签: python pytest


【解决方案1】:

简短的回答是,您无法按照自己的方式进行操作,即通过固定装置: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

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

此时您唯一真正的选择是在模块加载时或之前运行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

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

【讨论】:

  • 感谢您的详细回答,但我已经更新了我的另一个问题:我的问题是create_file_fixture 依赖于其他夹具,因此不能变成正常功能......我猜我必须找到其他东西或尝试改变我的项目的内部结构......
  • 你也可以考虑更新 pytest。虽然很高兴知道您预先进行了多少测试,但我不确定我是否有充分的理由不支持至少惰性生成器作为参数。
【解决方案2】:

这似乎与this question 重复。

按照设计,这是不可能的:“fixtures”的概念是为测试执行保留的,而“parameters”的概念是为测试集合保留的。请看my detailed answer here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多