考虑以下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 fixture timeline
,它本身具有属性instances
。此属性在测试中迭代,因此只有在instance
中每个timeline.instances
的断言成立时,测试才会通过。
然而,我真正想要做的是生成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中,函数'变为'它的返回值,但在测试参数化时似乎还没有发生。如何以理想的方式设置测试?
答案 0 :(得分:28)
这实际上可以通过indirect parametrization。
此示例使用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__])
答案 1 :(得分:11)
除了间接参数化或我下面涉及继承的hacky解决方案之外,您还可以仅将params
的参数@pytest.fixture()
使用-我认为这也许是最简单的解决方案?
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/fixture.html#parametrizing-fixtures
答案 2 :(得分:5)
从https://bitbucket.org/pytest-dev/pytest/issues/349/using-fixtures-in-pytestmarkparametrize开始,似乎无法在pytest.mark.parametrize
中使用灯具。
答案 3 :(得分:4)
使用间接参数化是可行的,但是我发现有必要使用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
标志。
答案 4 :(得分:0)
这是一个令人困惑的话题,因为人们倾向于认为固定装置和参数是同一回事。它们不是,甚至在pytest运行的同一阶段也不收集。按照设计,参数是在收集(正在构建测试节点的列表)时收集的,而夹具是在测试节点运行时执行的(测试节点列表是固定的,不能修改)。因此,夹具内容不能用于更改测试节点的数量-这是参数的作用。另请参阅我的detailed answer 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's answer提到了恕我直言,在此添加了另一个主题,因为它与您的问题没有直接关系。既然提到了它,甚至被接受为解决方案,那么让我详细说明一下:实际上,在pytest中,您不能在@pytest.mark.parametrize
中使用固定装置。但是即使可以,也不会改变任何东西。
由于此功能现已在pytest-cases
中作为Beta版提供(我是作者),因此您可以尝试一下。使示例即使具有该功能也可以工作的唯一方法仍然是相同的:从灯具中删除列表。因此,您最终得到:
import pytest
from pytest_cases import pytest_parametrize_plus, fixture_ref
class TimeLine(object):
instances = [0, 1, 2]
@pytest.fixture(params=TimeLine().instances)
def timeline(request):
return request.param
@pytest_parametrize_plus("t", [fixture_ref(timeline)])
def test_timeline(t):
assert t % 2 == 0
与前面的示例相同,但有一个额外的层,可能没有用。 注意:另请参见this discussion。
答案 5 :(得分:0)
您正在参数化参数集正在引用该函数。也许您的意思是引用Timeline.instances,而不是Fixture函数/ 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
答案 6 :(得分: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