繁体   English   中英

如何参数化 Pytest 夹具

[英]How to parametrize a Pytest fixture

考虑以下 Pytest:

import pytest

class TimeLine(object):
    instances = [0, 1, 2]

@pytest.fixture
def timeline():
    return TimeLine()

def test_timeline(timeline):
    for instance in timeline.instances:
        assert instance % 2 == 0

if __name__ == "__main__":
    pytest.main([__file__])

测试test_timeline使用 Pytest 固定装置timeline ,它本身具有属性instances 该属性在测试中被迭代,因此只有当该断言对timeline.instances .instances 中的每个instance成立时,测试才会通过。

然而,我真正想做的是生成 3 个测试,其中 2 个应该通过,其中 1 个将失败。 我试过了

@pytest.mark.parametrize("instance", timeline.instances)
def test_timeline(timeline):
    assert instance % 2 == 0

但这导致

AttributeError: 'function' object has no attribute 'instances'

据我了解,在 Pytest 固定装置中,函数“变成”了它的返回值,但是在测试参数化时这似乎还没有发生。 如何以所需的方式设置测试?

这实际上可以通过间接参数化来实现

此示例使用 pytest 3.1.2 执行您想要的操作:

import pytest

class TimeLine:
    def __init__(self, instances):
        self.instances = instances

@pytest.fixture
def timeline(request):
    return TimeLine(request.param)

@pytest.mark.parametrize(
    'timeline',
    ([1, 2, 3], [2, 4, 6], [6, 8, 10]),
    indirect=True
)
def test_timeline(timeline):
    for instance in timeline.instances:
        assert instance % 2 == 0

if __name__ == "__main__":
    pytest.main([__file__])

另请参阅此类似问题

而不是间接参数化,或者我下面涉及继承的hacky解决方案,你也可以只使用@pytest.fixture()params参数——我认为这可能是最简单的解决方案?

import pytest

class TimeLine:
    def __init__(self, instances=[0, 0, 0]):
        self.instances = instances


@pytest.fixture(params=[
    [1, 2, 3], [2, 4, 5], [6, 8, 10]
])
def timeline(request):
    return TimeLine(request.param)


def test_timeline(timeline):
    for instance in timeline.instances:
        assert instance % 2 == 0

https://docs.pytest.org/en/latest/how-to/fixtures.html#parametrizing-fixtures

这是一个令人困惑的话题,因为人们往往认为灯具和参数是一回事。 它们不是,甚至不会在 pytest 运行的同一阶段收集。 按照设计,参数是在收集测试时收集的(测试节点列表正在构建中),而夹具则是在测试节点运行执行(测试节点列表是固定的,不能修改)。 所以夹具内容不能用来改变测试节点的数量——这就是参数的作用。 另请参阅我的详细答案here

这就是为什么您的问题没有“原样”解决方案的原因:您应该将Timeline实例放在参数中,而不是固定装置中。 像这样:

import pytest

class TimeLine(object):
    instances = [0, 1, 2]

@pytest.fixture(params=TimeLine().instances)
def timeline(request):
    return request.param

def test_timeline(timeline):
    assert timeline % 2 == 0

Kurt Peek 的回答提到了另一个主题,恕我直言在这里增加了混淆,因为它与您的问题没有直接关系。 由于它被提及,甚至被接受为解决方案,让我详细说明:确实在 pytest 中,您不能在@pytest.mark.parametrize使用夹具。 但即使你能做到,也不会改变任何事情。

由于此功能现在在pytest-cases作为测试版提供(我是作者),您可以自己尝试一下。 即使使用该功能,使您的示例工作的唯一方法仍然相同:从夹具中删除列表。 所以你最终得到:

import pytest
from pytest_cases import parametrize, fixture_ref

class TimeLine(object):
    instances = [0, 1, 2]

@pytest.fixture(params=TimeLine().instances)
def timeline(request):
    return request.param

@parametrize("t", [fixture_ref(timeline)])
def test_timeline(t):
    assert t % 2 == 0

这与前面的示例相同,但有一个额外的,可能没用的层。 注意:另请参阅此讨论

Using fixtures in pytest.mark.parametrize pytest issue 来看,目前似乎无法在pytest.mark.parametrize使用 fixtures。

间接参数化的使用是有效的,但我发现需要使用request.param作为一个神奇的、未命名的变量有点尴尬。

这是我使用过的一种模式。 可以说,它以不同的方式尴尬,但也许你也会更喜欢它!

import pytest

class TimeLine:
    def __init__(self, instances):
        self.instances = instances


@pytest.fixture
def instances():
    return [0, 0, 0]


@pytest.fixture
def timeline(instances):
    return TimeLine(instances)


@pytest.mark.parametrize('instances', [
    [1, 2, 3], [2, 4, 5], [6, 8, 10]
])
def test_timeline(timeline):
    for instance in timeline.instances:
        assert instance % 2 == 0

timeline夹具依赖于另一个名为instances夹具,其默认值为[0,0,0] ,但在实际测试中,我们使用parametrize化为instances注入一系列不同的值。

在我看来,优点是一切都有一个好名字,而且您不需要传递该indirect=True标志。

您的参数化 paramset 正在引用该函数。 也许您的意思是引用 Timeline.instances,而不是夹具函数 /timeline/:

@pytest.mark.parametrize("instance", Timeline.instances)
def test_func(instance):
    pass

我认为您希望将标记 xfail 添加到一组参数中,也许是有原因的?

def make_my_tests():
    return [0, 2, mark.xfail(1, reason='not even')]

@mark.parametrize('timeline', paramset=make_my_tests(), indirect=True)
def test_func(timeline):
    assert timeline % 2 == 0

我解决了创建不带fixture装饰器的函数和应用装饰器的先前函数的问题:

def one_raw():
    return 1

one = pytest.fixture(one_raw)

def test_add(one):
    assert one + 2 == 3

@pytest.mark.parametrize("subtrahend", (one_raw(), int(True)))
def test_sub(subtrahend):
    assert 4 - subtrahend == 3

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM