如何将参数传递给 pytest 装置?

问题描述 投票:0回答:5

我所有测试的基准是总是有一辆出租车,里面至少有一名乘客。我可以使用一些基本的装置轻松实现此设置:

from blah import Passenger, Taxi

@pytest.fixture
def passenger():
    return Passenger()

@pytest.fixture
def taxi(passenger):
    return Taxi(rear_seat=passenger)

测试基线很简单:

def test_taxi_contains_passenger(taxi)
    assert taxi.has_passenger()

当我开始需要更复杂的测试设置时,我的问题就出现了。在某些情况下,我需要出租车载有多名乘客,在某些情况下,我需要定义乘客属性。例如:

def test_three_passengers_in_taxi(taxi)
    assert taxi.has_passengers(3)
    assert taxi.front_passenger_is_not_a_child()

我可以通过针对特定测试使用特定的固定装置来解决这个问题。对于上述测试,我将创建以下夹具:

@pytest.fixture
def three_passenger_test_setup(taxi)
    taxi.add_front_seat_passenger(Passenger(child=False))
    taxi.add_rear_seat_passenger(Passenger())
    return taxi

我可以将上述固定装置传递到我的测试用例中,一切都很好,但如果我沿着这条路线走下去,我最终可能会为每个测试提供一个固定装置,感觉应该有一种更有效的方法来做到这一点。

有没有办法将参数传递给固定装置,以便这些参数可以用于创建固定装置返回的对象?我应该参数化测试函数吗?固定装置?或者我是在浪费时间,并且每次测试都有一个固定装置是可行的方法吗?

python arguments pytest fixtures pytest-fixtures
5个回答
106
投票

我们可以通过使用一种在固定装置内获取参数并从固定装置返回该方法的方法来做到这一点。

我给你举个例子

@pytest.fixture
def my_fixture():

  def _method(a, b):
    return a*b

  return _method

def test_me(my_fixture):
  result1 = my_fixture(2, 3)
  assert result1 == 6

  result2 = my_fixture(4, 5)
  assert result2 == 20

17
投票

有没有办法将参数传递给固定装置,以便这些参数 可以用于创建夹具返回的对象吗? 我应该参数化测试函数吗?

您可以通过

indirect=True
使用测试参数化。 在 pytest 文档中:对特定参数应用间接。 如此处所示:https://stackoverflow.com/a/33879151/3858507


固定装置?

另一个可能适合您的选项是使用一些使用参数化指定参数的固定装置:

@pytest.fixture(params=[3,4])
def number_of_passengers(request):
    return request.param

然后从出租车和测试本身访问此固定装置:

@pytest.fixture
def taxi(number_of_passengers):
    return Taxi(rear_seat=Passenger() * number_of_passengers)

def test_three_passengers_in_taxi(taxi, number_of_passengers)
    assert taxi.has_passengers(number_of_passengers)
    assert taxi.front_passenger_is_not_a_child()

如果您的测试和断言在您拥有的案例之间非常相似,那么这种方法很好。


或者我是在浪费时间吗?每次测试都有一个固定装置是正确的选择吗?

我想说你绝对不应该为每个测试函数创建一个夹具。为此,您只需将设置放入测试中即可。如果您必须针对出租车的不同情况做出不同的断言,这实际上是一个可行的替代方案。


最后,您可以使用的另一种可能的模式是出租车工厂。虽然您提供的示例不太有用,但如果需要多个参数并且只有一些参数发生变化,您可以创建类似于以下内容的夹具:

from functools import partial @pytest.fixture def taxi_factory(): return partial(Taxi, 1, 2, 3)
    

8
投票
那个

fixture

 只是一个 Python 装饰器。

@decorator def function(args): ...

很喜欢

def function(args): ... function = decorator(function)

所以你也许可以编写自己的装饰器,将你想要装饰的函数包装在你需要的任何内容中

fixture

def myFixture(parameter): def wrapper(function): def wrapped(*args, **kwargs): return function(parameter, *args, **kwargs) return wrapped return pytest.fixture(wrapper) @myFixture('foo') def function(parameter, ...): ...

这将像

fixture

 一样,但会将值 (
'foo'
) 作为 
parameter
 传递给 
function


7
投票
TLDR;使用

pytest.mark

request
 夹具访问 
request.keywords

这是一个非常老的问题,但现有的答案对我来说不起作用,所以这是我使用 pytest.mark 的解决方案

from blah import Passenger, Taxi @pytest.fixture def passenger(): return Passenger() @pytest.fixture def taxi(passenger, request): if "taxi" in request.keywords: kwargs = request.keywords["taxi"].kwargs else: kwargs = dict(rear_seat=passenger) return Taxi(**kwargs) # This allows testing the baseline as-is... def test_taxi_contains_passenger(taxi) assert taxi.has_passenger() # and also using pytest.mark to pass whatever kwargs: @pytest.mark.taxi(rear_seat=[Passenger()] * 3) def test_three_passengers_in_taxi(taxi) assert taxi.has_passengers(3) assert taxi.front_passenger_is_not_a_child()
    

0
投票
例如,您可以将 2 个参数传递给具有嵌套函数

addition()

core()
 固定装置,如下所示:

import pytest @pytest.fixture def addition(): def core(num1, num2): return num1 + num2 return core def test(request, addition): print(addition(2, 3)) print(request.getfixturevalue("addition")(6, 8)) assert True
输出:

$ pytest -q -rP . [100%] =============== PASSES ================ ________________ test _________________ -------- Captured stdout call --------- 5 14 1 passed in 0.10s
    
© www.soinside.com 2019 - 2024. All rights reserved.