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.