Mise à jour: Étant donné que c'est la réponse acceptée à cette question et qu'elle est toujours votée parfois, je devrais ajouter une mise à jour. Bien que ma réponse originale (ci-dessous) était le seul moyen de le faire dans les anciennes versions de pytest, comme d' autres l' ont noté, pytest prend désormais en charge la paramétrisation indirecte des appareils. Par exemple, vous pouvez faire quelque chose comme ceci (via @imiric):
# test_parameterized_fixture.py
import pytest
class MyTester:
def __init__(self, x):
self.x = x
def dothis(self):
assert self.x
@pytest.fixture
def tester(request):
"""Create tester object"""
return MyTester(request.param)
class TestIt:
@pytest.mark.parametrize('tester', [True, False], indirect=['tester'])
def test_tc1(self, tester):
tester.dothis()
assert 1
$ pytest -v test_parameterized_fixture.py
================================================================================= test session starts =================================================================================
platform cygwin -- Python 3.6.8, pytest-5.3.1, py-1.8.0, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: .
collected 2 items
test_parameterized_fixture.py::TestIt::test_tc1[True] PASSED [ 50%]
test_parameterized_fixture.py::TestIt::test_tc1[False] FAILED
Cependant, bien que cette forme de paramétrisation indirecte soit explicite, comme le souligne @Yukihiko Shinoda , elle prend désormais en charge une forme de paramétrisation indirecte implicite (bien que je n'ai pas trouvé de référence évidente à cela dans la documentation officielle):
# test_parameterized_fixture2.py
import pytest
class MyTester:
def __init__(self, x):
self.x = x
def dothis(self):
assert self.x
@pytest.fixture
def tester(tester_arg):
"""Create tester object"""
return MyTester(tester_arg)
class TestIt:
@pytest.mark.parametrize('tester_arg', [True, False])
def test_tc1(self, tester):
tester.dothis()
assert 1
$ pytest -v test_parameterized_fixture2.py
================================================================================= test session starts =================================================================================
platform cygwin -- Python 3.6.8, pytest-5.3.1, py-1.8.0, pluggy-0.13.1 -- /usr/bin/python3
cachedir: .pytest_cache
rootdir: .
collected 2 items
test_parameterized_fixture2.py::TestIt::test_tc1[True] PASSED [ 50%]
test_parameterized_fixture2.py::TestIt::test_tc1[False] FAILED
Je ne sais pas exactement quelle est la sémantique de cette forme, mais il semble que cela pytest.mark.parametrize
reconnaisse que bien que la test_tc1
méthode ne prenne pas d'argument nommé tester_arg
, l' tester
appareil qu'elle utilise le fait, donc il transmet l'argument paramétré à travers l' tester
appareil.
J'ai eu un problème similaire - j'ai un appareil appelé test_package
, et je voulais plus tard pouvoir passer un argument optionnel à cet appareil lors de son exécution dans des tests spécifiques. Par exemple:
@pytest.fixture()
def test_package(request, version='1.0'):
...
request.addfinalizer(fin)
...
return package
(Peu importe à ces fins ce que fait l'appareil ou le type d'objet renvoyé package
).
Il serait alors souhaitable d'utiliser d'une manière ou d'une autre cet appareil dans une fonction de test de telle sorte que je puisse également spécifier le version
argument de cet appareil à utiliser avec ce test. Ce n'est actuellement pas possible, mais pourrait constituer une fonctionnalité intéressante.
En attendant, il était assez facile de faire en sorte que mon appareil renvoie simplement une fonction qui effectue tout le travail effectué précédemment par l'appareil, mais me permet de spécifier l' version
argument:
@pytest.fixture()
def test_package(request):
def make_test_package(version='1.0'):
...
request.addfinalizer(fin)
...
return test_package
return make_test_package
Maintenant, je peux utiliser ceci dans ma fonction de test comme:
def test_install_package(test_package):
package = test_package(version='1.1')
...
assert ...
etc.
La solution tentée par l'OP allait dans la bonne direction, et comme le suggère la réponse de @ hpk42 , le MyTester.__init__
pourrait simplement stocker une référence à la demande comme:
class MyTester(object):
def __init__(self, request, arg=["var0", "var1"]):
self.request = request
self.arg = arg
# self.use_arg_to_init_logging_part()
def dothis(self):
print "this"
def dothat(self):
print "that"
Ensuite, utilisez ceci pour implémenter l'appareil comme:
@pytest.fixture()
def tester(request):
""" create tester object """
# how to use the list below for arg?
_tester = MyTester(request)
return _tester
Si vous le souhaitez, la MyTester
classe pourrait être un peu restructurée afin que son .args
attribut puisse être mis à jour après sa création, afin de modifier le comportement des tests individuels.
Ceci est en fait pris en charge nativement dans py.test via une paramétrisation indirecte .
Dans votre cas, vous auriez:
la source
indirect
argument du mot - clé est certes rare et peu conviviale, ce qui explique probablement l'obscurité de cette technique essentielle. J'ai parcouru le site py.test à plusieurs reprises à la recherche de cette fonctionnalité - seulement pour devenir vide, plus ancien et embrouillé. L'amertume est un lieu connu sous le nom d'intégration continue. Merci Odin pour Stackoverflow.test_tc1
devienttest_tc1[tester0]
.indirect=True
remet les paramètres à tous les appareils appelés, non? Parce que la documentation nomme explicitement les appareils pour la paramétrisation indirecte, par exemple pour un appareil nomméx
:indirect=['x']
Vous pouvez accéder au module / à la classe / à la fonction de demande à partir des fonctions de fixation (et donc à partir de votre classe Tester), voir interagir avec la demande de contexte de test à partir d'une fonction de fixation . Ainsi, vous pouvez déclarer certains paramètres sur une classe ou un module et l'appareil de test peut le récupérer.
la source
@fixture def my_fixture(request)
et ensuite@pass_args(arg1=..., arg2=...) def test(my_fixture)
et obtenir ces argumentsmy_fixture()
comme ceciarg1 = request.arg1, arg2 = request.arg2
. Est-ce que quelque chose comme ça est possible dans py.test maintenant?Je n'ai trouvé aucun document, cependant, il semble fonctionner dans la dernière version de pytest.
la source
Nadège
été annulé. Ainsi, cette fonctionnalité non documentée (je pense qu'elle est toujours non documentée?) Vit toujours.Pour améliorer un peu la réponse d'imiric : une autre manière élégante de résoudre ce problème est de créer des "fixtures de paramètres". Personnellement, je le préfère à la
indirect
fonctionnalité depytest
. Cette fonctionnalité est disponible à partir depytest_cases
, et l'idée originale a été suggérée par Sup3rGeo .Notez que
pytest-cases
fournit également@pytest_fixture_plus
que vous permettre d'utiliser les marques de paramétrisation sur vos appareils, et@cases_data
qui vous permettent à la source de vos paramètres de fonctions dans un module distinct. Voir doc pour plus de détails. Je suis l'auteur au fait;)la source
param_fixture
. Voyez cette réponse . Je n'ai cependant pas trouvé d'exemple comme celui-ci dans la documentation; Sais tu quelque chose à propos de cela?J'ai fait un décorateur amusant qui permet d'écrire des accessoires comme celui-ci:
Ici, à gauche de
/
vous avez d'autres appareils, et à droite vous avez des paramètres qui sont fournis en utilisant:Cela fonctionne de la même manière que les arguments de fonction. Si vous ne fournissez pas l'
age
argument, celui par défaut,,69
est utilisé à la place. si vous ne fournissez pasname
ou omettez ledog.arguments
décorateur, vous obtenez le régulierTypeError: dog() missing 1 required positional argument: 'name'
. Si vous avez un autre appareil qui prend la disputename
, il n'est pas en conflit avec celui-ci.Les appareils Async sont également pris en charge.
De plus, cela vous donne un bon plan d'installation:
Un exemple complet:
Le code pour le décorateur:
la source