[英]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.