如何在py.test中将几个参数化夹具连接成一个新的夹具?

Mat*_*ias 10 python fixtures pytest

如果我有两个参数化夹具,我怎么能创建一个单独的测试函数,首先用一个夹具的实例调用,然后用另一个夹具的实例调用?

我想创建一个以某种方式连接两个现有灯具的新灯具是有意义的.这适用于"普通"灯具,但我似乎无法使用参数化灯具.

这是我尝试过的简化示例:

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
Run Code Online (Sandbox Code Playgroud)

当我运行这个时,我收到此错误:

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'
Run Code Online (Sandbox Code Playgroud)

...和相同的错误upper().

我做错了什么?

我怎样才能解决这个问题?


更新:

有一个PyTest插件可以用来解决这个问题:https://github.com/TvoroG/pytest-lazy-fixture.

pip-installing这个插件,上述代码的唯一必要的改变如下:

@pytest.fixture(params=[pytest.lazy_fixture('lower'),
                        pytest.lazy_fixture('upper')])
def all(request):
    return request.param
Run Code Online (Sandbox Code Playgroud)

但请注意,与当前的PyTest版本3.6.3不兼容,请参阅https://github.com/TvoroG/pytest-lazy-fixture/pull/27.

相关的PyTest问题:

sma*_*rie 8

There is now a solution available in pytest-cases, named fixture_union. Here is how you create the fixture union that you requested in your example:

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)
Run Code Online (Sandbox Code Playgroud)

It works as expected:

<...>::test_all[lower-1] 
<...>::test_all[lower-2] 
<...>::test_all[lower-3] 
<...>::test_all[upper-1] 
<...>::test_all[upper-2] 
Run Code Online (Sandbox Code Playgroud)

Note that I used pytest_fixture_plus in the above example because if you use pytest.fixture you will have to handle yourself the cases where a fixture is not actually used. This is done as follows, for example for the upper fixture:

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
Run Code Online (Sandbox Code Playgroud)

See documentation for details. (I'm the author by the way ;) )


小智 1

它并不美丽,但今天您可能知道更好的方法。

“所有”装置内的请求对象只知道自己的参数:“下”、“上”。使用固定装置功能中的固定装置的一种方法。

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, lower, upper):
    if request.param == 'lower':
        return lower
    else:
        return upper

def test_all(all):
    assert 0, all
Run Code Online (Sandbox Code Playgroud)

  • 感谢你的回答!但是使用“lower”和“upper”作为“all()”的函数参数运行“lower()”和“upper()”的所有组合(我猜更准确地说是笛卡尔积)的测试,即 6 次。将两个“params”添加到“all()”会使数字加倍,即“test_all()”运行 12 次。我只想将 `lower()` 和 `upper()` 连接起来,即只运行测试 5 次。 (7认同)