Python 如何在py.test中将多个参数化夹具连接到一个新夹具中?
如果我有两个参数化的fixture,我如何创建一个单独的测试函数,首先用一个fixture的实例调用,然后用另一个fixture的实例调用 我想创建一个新的装置,以某种方式连接两个现有装置是有意义的。这对“正常”装置很有效,但我似乎无法使其与参数化装置一起工作 下面是我尝试的一个简化示例:Python 如何在py.test中将多个参数化夹具连接到一个新夹具中?,python,fixtures,pytest,Python,Fixtures,Pytest,如果我有两个参数化的fixture,我如何创建一个单独的测试函数,首先用一个fixture的实例调用,然后用另一个fixture的实例调用 我想创建一个新的装置,以某种方式连接两个现有装置是有意义的。这对“正常”装置很有效,但我似乎无法使其与参数化装置一起工作 下面是我尝试的一个简化示例: import pytest @pytest.fixture(params=[1, 2, 3]) def lower(request): return "i" * request.param @py
import pytest
@pytest.fixture(params=[1, 2, 3])
def lower(request):
return "i" * request.param
@pytest.fixture(params=[1, 2])
def upper(request):
return "I" * request.param
@pytest.fixture(params=['lower', 'upper'])
def all(request):
return request.getfuncargvalue(request.param)
def test_all(all):
assert 0, all
当我运行此命令时,会出现以下错误:
request = <SubRequest 'lower' for <Function 'test_all[lower]'>>
@pytest.fixture(params=[1, 2, 3])
def lower(request):
> return "i" * request.param
E AttributeError: 'SubRequest' object has no attribute 'param'
但是,请注意,在一些复杂的情况下,它将不起作用:
相关测试问题:
- 它并不美丽,但也许今天你知道更好的方法
“all”装置内的请求对象只知道自己的参数:“lower”、“upper”。单程
我得到了完全相同的结果(并且收到了一份类似但不同的报告)。我能想出的最好的解决办法是重新考虑如何将我的测试参数化。我没有使用具有兼容输出的多个fixture,而是将fixture用作常规函数,并将元fixture参数化以接受函数名和参数:
import pytest
def lower(n):
return 'i' * n
def upper(n):
return 'I' * n
@pytest.fixture(params=[
(lower, 1),
(lower, 2),
(upper, 1),
(upper, 2),
(upper, 3),
])
def all(request):
func, *n = request.param
return func(*n)
def test_all(all):
...
在您的特定情况下,将n
解包到一个列表中并将其与*
一起传递有点过分,但它提供了通用性。我的案例中的装置都接受不同的参数列表
在pytest允许我们正确地链接夹具之前,这是我提出的唯一方法,可以在您的情况下运行5个测试,而不是12个。您可以使用以下方法缩短列表:
@pytest.fixture(params=[
*[(lower, i) for i in range(1, 3)],
*[(upper, i) for i in range(1, 4)],
])
这样做有一个实际的好处。如果管道中有其他依赖项,您可以选择要对哪些测试执行特殊操作,如XFAIL,而不影响整个其他测试。现在在
pytestcases
中有一个解决方案,名为fixture\u union
。以下是如何创建示例中请求的装置联合:
from pytest_cases import fixture_union, pytest_fixture_plus
@pytest_fixture_plus(params=[1, 2, 3])
def lower(request):
return "i" * request.param
@pytest_fixture_plus(params=[1, 2])
def upper(request):
return "I" * request.param
fixture_union('all', ['lower', 'upper'])
def test_all(all):
print(all)
它按预期工作:
<...>::test_all[lower-1]
<...>::test_all[lower-2]
<...>::test_all[lower-3]
<...>::test_all[upper-1]
<...>::test_all[upper-2]
有关详细信息,请参阅。(顺便说一句,我是作者;)我看到py.test tracker上有一个可能解决我问题的插件,但是py.test开发人员还没有响应。有一个插件似乎与我的问题有关,但也没有响应……插件允许您这样做。@ChristianLong感谢您的提示!我已经在上面添加了一些信息。谢谢你的回答!但是使用
lower
和upper
作为all()
的函数参数会对lower()
和upper()
的所有组合(我想更准确地说是笛卡尔积)运行测试,即6次。将两个params
添加到all()
将使数字加倍,即test\u all()
运行12次。我只希望将lower()
和upper()
连接起来,即只运行测试5次。那么夹具的要点是它们可以重复使用,对吗?如果我创建一个参数化的装置,我还想重复使用参数化(因为它是装置的一部分)。按照你的建议,我不能重复使用参数化。假设有更多的装置,如下部
和上部
,我想将它们组合成几种不同的装置。这个插件让我无需手动重复任何操作就可以做到这一点。
from pytest_cases import fixture_union, pytest_fixture_plus
@pytest_fixture_plus(params=[1, 2, 3])
def lower(request):
return "i" * request.param
@pytest_fixture_plus(params=[1, 2])
def upper(request):
return "I" * request.param
fixture_union('all', ['lower', 'upper'])
def test_all(all):
print(all)
<...>::test_all[lower-1]
<...>::test_all[lower-2]
<...>::test_all[lower-3]
<...>::test_all[upper-1]
<...>::test_all[upper-2]
import pytest
from pytest_cases import NOT_USED
@pytest.fixture(params=[1, 2])
def upper(request):
# this fixture does not use pytest_fixture_plus
# so we have to explicitly discard the 'NOT_USED' cases
if request.param is not NOT_USED:
return "I" * request.param