Update: Da dies die akzeptierte Antwort auf diese Frage ist und manchmal immer noch positiv bewertet wird, sollte ich ein Update hinzufügen. Obwohl meine ursprüngliche Antwort (unten) die einzige Möglichkeit war, dies in älteren Versionen von pytest zu tun, wie andere festgestellt haben , unterstützt pytest jetzt die indirekte Parametrisierung von Vorrichtungen. Zum Beispiel können Sie so etwas tun (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
Obwohl diese Form der indirekten Parametrisierung explizit ist, unterstützt @Yukihiko Shinoda, wie sie betont , jetzt eine Form der impliziten indirekten Parametrisierung (obwohl ich in den offiziellen Dokumenten keinen offensichtlichen Hinweis darauf finden konnte):
# 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
Ich weiß nicht genau, wie die Semantik dieses Formulars aussieht, aber es scheint zu pytest.mark.parametrize
erkennen, dass die test_tc1
Methode zwar kein Argument mit dem Namen verwendet tester_arg
, das verwendete tester
Gerät dies jedoch tut, sodass das parametrisierte Argument durch das tester
Gerät weitergeleitet wird.
Ich hatte ein ähnliches Problem - ich habe ein Fixture namens test_package
und wollte später in der Lage sein, diesem Fixture ein optionales Argument zu übergeben, wenn es in bestimmten Tests ausgeführt wird. Beispielsweise:
@pytest.fixture()
def test_package(request, version='1.0'):
...
request.addfinalizer(fin)
...
return package
(Für diese Zwecke spielt es keine Rolle, was das Gerät tut oder welche Art von Objekt das zurückgegebene ist package
).
Es wäre dann wünschenswert, dieses Gerät irgendwie in einer Testfunktion so zu verwenden, dass ich auch das version
Argument für dieses Gerät angeben kann, das mit diesem Test verwendet werden soll. Dies ist derzeit nicht möglich, könnte aber eine nette Funktion sein.
In der Zwischenzeit war es einfach genug, mein Gerät dazu zu bringen, einfach eine Funktion zurückzugeben , die die gesamte Arbeit des Geräts erledigt, aber mir erlaubt, das folgende version
Argument anzugeben :
@pytest.fixture()
def test_package(request):
def make_test_package(version='1.0'):
...
request.addfinalizer(fin)
...
return test_package
return make_test_package
Jetzt kann ich dies in meiner Testfunktion verwenden wie:
def test_install_package(test_package):
package = test_package(version='1.1')
...
assert ...
und so weiter.
Der Lösungsversuch des OP ging in die richtige Richtung, und wie aus der Antwort von @ hpk42 hervorgeht , MyTester.__init__
könnte der Verweis auf die Anfrage einfach wie folgt gespeichert werden:
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"
Verwenden Sie dies dann, um das Gerät wie folgt zu implementieren:
@pytest.fixture()
def tester(request):
""" create tester object """
# how to use the list below for arg?
_tester = MyTester(request)
return _tester
Falls gewünscht, kann die MyTester
Klasse ein wenig umstrukturiert werden, damit ihr .args
Attribut nach dem Erstellen aktualisiert werden kann, um das Verhalten für einzelne Tests zu optimieren.
Dies wird in py.test über die indirekte Parametrisierung nativ unterstützt .
In Ihrem Fall hätten Sie:
quelle
indirect
Schlüsselwortargument ist zugegebenermaßen spärlich und unfreundlich, was wahrscheinlich die Unklarheit dieser wesentlichen Technik erklärt. Ich habe die py.test-Site mehrmals nach genau dieser Funktion durchsucht - nur um leer, älter und verwirrt zu erscheinen. Bitterkeit ist ein Ort, der als kontinuierliche Integration bekannt ist. Danke Odin für Stackoverflow.test_tc1
wirdtest_tc1[tester0]
.indirect=True
Parameter an alle genannten Geräte, oder? Weil die Dokumentation die Geräte für die indirekte Parametrisierung explizit benennt, z. B. für ein Gerät mit dem Namenx
:indirect=['x']
Sie können über Fixture-Funktionen (und damit über Ihre Tester-Klasse) auf das anfordernde Modul / die anfordernde Funktion / Funktion zugreifen. Weitere Informationen finden Sie unter Interaktion mit dem anfordernden Testkontext über eine Fixture-Funktion . Sie können also einige Parameter für eine Klasse oder ein Modul deklarieren und das Testgerät kann sie abrufen.
quelle
@fixture def my_fixture(request)
und dann@pass_args(arg1=..., arg2=...) def test(my_fixture)
und diese args in bekommenmy_fixture()
wie diesesarg1 = request.arg1, arg2 = request.arg2
. Ist so etwas jetzt in py.test möglich?Ich konnte kein Dokument finden, es scheint jedoch in der neuesten Version von pytest zu funktionieren.
quelle
Nadège
zurückgesetzt wurde. Somit lebt diese undokumentierte Funktion (ich denke, sie ist immer noch undokumentiert?) Noch.Um die Antwort von imiric ein wenig zu verbessern : Eine andere elegante Möglichkeit, dieses Problem zu lösen, besteht darin, "Parameter-Fixtures" zu erstellen. Ich persönlich bevorzuge es gegenüber der
indirect
Funktion vonpytest
. Diese Funktion ist bei erhältlichpytest_cases
und die ursprüngliche Idee wurde von Sup3rGeo vorgeschlagen .Beachten Sie, dass
pytest-cases
auch bietet@pytest_fixture_plus
, mit denen Sie Parametrisierung Markierungen auf Fixtures verwenden, und@cases_data
, mit denen Sie Ihre Parameter von Funktionen in einem separaten Modul beziehen. Siehe Dokument für Details. Ich bin übrigens der Autor;)quelle
param_fixture
. Siehe diese Antwort . Ich konnte jedoch kein solches Beispiel in den Dokumenten finden. Weißt du etwas darüber?Ich habe einen lustigen Dekorateur gemacht, mit dem man solche Geräte schreiben kann:
Hier
/
haben Sie links von Ihnen andere Geräte und rechts haben Sie Parameter, die geliefert werden mit:Dies funktioniert genauso wie Funktionsargumente. Wenn Sie das
age
Argument nicht angeben69
, wird stattdessen das Standardargument verwendet. Wenn Siename
dendog.arguments
Dekorateur nicht liefern oder weglassen , erhalten Sie den regulärenTypeError: dog() missing 1 required positional argument: 'name'
. Wenn Sie ein anderes Gerät haben, das Argumente benötigtname
, steht es nicht in Konflikt mit diesem.Asynchrone Geräte werden ebenfalls unterstützt.
Darüber hinaus erhalten Sie einen schönen Einrichtungsplan:
Ein vollständiges Beispiel:
Der Code für den Dekorateur:
quelle