web-dev-qa-db-fra.com

Puis-je passer des arguments aux appareils les plus fiables?

La base de tous mes tests est qu’il y aura toujours un taxi avec au moins un passager. Je peux facilement réaliser cette configuration avec quelques appareils de base:

from blah import Passenger, Taxi

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

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

Tester la base est simple:

def test_taxi_contains_passenger(taxi)
    assert taxi.has_passenger()

Mon problème surgit lorsque je commence à avoir besoin d'une configuration de test plus complexe. Il y aura des scénarios dans lesquels j'aurai besoin d'un taxi pour avoir plus d'un passager et des scénarios dans lesquels je devrai définir les attributs du passager. Par exemple:

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

Je peux contourner ce problème en ayant des montages spécifiques pour des tests spécifiques. Pour le test ci-dessus, je créerais le projecteur suivant:

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

Je peux faire passer le montage ci-dessus à mon cas de test et tout est dandy, mais si je m'engage dans cette voie, je pourrais me retrouver avec un montage pour chaque test et il semble qu'il devrait y avoir un moyen plus efficace de le faire.

Existe-t-il un moyen de passer des arguments à un appareil afin que ces arguments puissent être utilisés lors de la création de l'objet renvoyé par l'appareil? Devrais-je paramétrer la fonction de test? Le luminaire? Ou est-ce que je perds du temps et est-ce qu'un montage par test est la voie à suivre?

6
pyzzled

Nous pouvons faire cela en utilisant une méthode qui prend args dans un fixture et renvoie la méthode depuis le fixture. 

permettez-moi de vous montrer un exemple

@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
12
supamaze

Existe-t-il un moyen de passer des arguments à un appareil afin que ces arguments Puissent être utilisés dans la création de l'objet renvoyé par l'appareil? Devrais-je paramétrer la fonction de test? 

Vous pouvez utiliser la paramétrisation de test avec indirect=True. Dans la documentation pytest: Appliquez indirectement sur des arguments particuliers . Comme indiqué ici: https://stackoverflow.com/a/33879151/3858507


Le luminaire?

Une autre option qui pourrait vous convenir est d'utiliser un appareil qui spécifie l'argument en utilisant la paramétrisation:

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

puis accéder à cet appareil depuis le taxi et le test lui-même:

@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()

Cette méthode est utile si vos tests et vos affirmations sont très similaires entre les cas que vous avez.


Ou est-ce que je perds du temps et est-ce qu'un montage par test est la voie à suivre?

Je dirais que vous ne devriez pas créez un appareil pour chaque fonction de test. Pour cela, vous pouvez simplement mettre la configuration dans le test. C'est en fait une alternative viable dans le cas où vous devez faire des affirmations différentes pour différents cas de taxi.


Et enfin, un autre modèle possible que vous pouvez utiliser est une usine de taxis. Tandis que pour l'exemple que vous avez présenté, ce n'est pas très utile, si plusieurs paramètres sont requis et que seuls certains changent, vous pouvez créer un appareil similaire au suivant:

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

Cette fixture est juste un décorateur Python.

@decorator
def function(args):
  ...

est chic pour

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

Ainsi, vous pourrez peut-être écrire votre propre décorateur, en résumant la fonction que vous souhaitez décorer selon vos besoins et la 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, ...):
  ...

Ceci agira comme le fixture mais passera une valeur ('foo') comme parameter à function.

0
Alfe