Aktualizacja: Ponieważ jest to zaakceptowana odpowiedź na to pytanie i czasami jest przegłosowana, powinienem dodać aktualizację. Mimo, że mój oryginalny odpowiedź (poniżej) było jedynym sposobem, aby to zrobić w starszych wersjach pytest jak inni nie zauważyć pytest teraz obsługuje pośrednie parametryzacji urządzeń. Na przykład możesz zrobić coś takiego (przez @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
Jednak, chociaż ta forma parametryzacji pośredniej jest wyraźna, jak wskazuje @Yukihiko Shinoda , obsługuje ona teraz formę niejawnej parametryzacji pośredniej (chociaż nie mogłem znaleźć żadnego oczywistego odniesienia do tego w oficjalnych dokumentach):
# 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
Nie wiem dokładnie, jaka jest semantyka tego formularza, ale wydaje się, że pytest.mark.parametrize
rozpoznaje, że chociaż test_tc1
metoda nie przyjmuje argumentu o nazwie tester_arg
, tester
urządzenie, którego używa, robi, więc przekazuje sparametryzowany argument dalej przez tester
urządzenie.
Miałem podobny problem - mam wywołane urządzenie test_package
, a później chciałem móc przekazać opcjonalny argument do tego urządzenia podczas uruchamiania go w określonych testach. Na przykład:
@pytest.fixture()
def test_package(request, version='1.0'):
...
request.addfinalizer(fin)
...
return package
(Dla tych celów nie ma znaczenia, co robi urządzenie lub jaki typ zwracanego obiektu package
).
Byłoby zatem pożądane, aby w jakiś sposób użyć tego urządzenia w funkcji testowej w taki sposób, żebym mógł również określić version
argument dla tego urządzenia do użycia w tym teście. Obecnie nie jest to możliwe, ale może być fajną funkcją.
W międzyczasie łatwo było sprawić, że moje urządzenie po prostu zwróciło funkcję, która wykonuje całą pracę, jaką wykonało wcześniej urządzenie, ale pozwala mi określić 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
Teraz mogę użyć tego w mojej funkcji testowej, takiej jak:
def test_install_package(test_package):
package = test_package(version='1.1')
...
assert ...
i tak dalej.
Próba rozwiązania OP zmierzała we właściwym kierunku i, jak sugeruje odpowiedź @ hpk42 , MyTester.__init__
można po prostu zapisać odniesienie do żądania, takie jak:
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"
Następnie użyj tego, aby zaimplementować urządzenie, takie jak:
@pytest.fixture()
def tester(request):
""" create tester object """
# how to use the list below for arg?
_tester = MyTester(request)
return _tester
W razie potrzeby MyTester
klasę można nieco przebudować, aby jej .args
atrybut mógł zostać zaktualizowany po jej utworzeniu, aby dostosować zachowanie dla poszczególnych testów.
W rzeczywistości jest to obsługiwane natywnie w py.test przez pośrednią parametryzację .
W twoim przypadku miałbyś:
źródło
indirect
argumentu słowa kluczowego jest wprawdzie rzadki i nieprzyjazny, który prawdopodobnie odpowiada za zapomnienie o tej podstawowej techniki. Wielokrotnie przeszukiwałem witrynę py.test w poszukiwaniu tej właśnie funkcji - tylko po to, by wydać się pusta, starsza i oszołomiona. Goryczka to miejsce znane jako ciągła integracja. Podziękuj Odinowi za Stackoverflow.test_tc1
staje siętest_tc1[tester0]
.indirect=True
przekazuje parametry wszystkim wywołanym urządzeniom, prawda? Ponieważ dokumentacja wyraźnie wymienia urządzenia do pośredniej parametryzacji, np. Dla urządzenia o nazwiex
:indirect=['x']
Możesz uzyskać dostęp do żądającego modułu / klasy / funkcji z funkcji urządzenia (a tym samym z klasy Tester), zobacz interakcję z żądaniem kontekstu testowego z funkcji urządzenia . Możesz więc zadeklarować niektóre parametry w klasie lub module, a urządzenie testujące może je odebrać.
źródło
@fixture def my_fixture(request)
a następnie@pass_args(arg1=..., arg2=...) def test(my_fixture)
uzyskać te argumenty wmy_fixture()
ten sposóbarg1 = request.arg1, arg2 = request.arg2
. Czy coś takiego jest teraz możliwe w py.test?Nie mogłem znaleźć żadnego dokumentu, jednak wygląda na to, że działa w najnowszej wersji pytest.
źródło
Nadège
został cofnięty. Tak więc ta nieudokumentowana funkcja (myślę, że nadal nie jest udokumentowana?) Nadal istnieje.Aby nieco poprawić odpowiedź imirica : innym eleganckim sposobem rozwiązania tego problemu jest utworzenie „ ustawiania parametrów”. Osobiście wolę to od
indirect
funkcjipytest
. Ta funkcja jest dostępna odpytest_cases
, a oryginalny pomysł zasugerował Sup3rGeo .Zauważ, że
pytest-cases
zapewnia również@pytest_fixture_plus
możliwość używania znaczników parametryzacji w urządzeniach, a także@cases_data
umożliwia pobieranie parametrów z funkcji w oddzielnym module. Szczegółowe informacje można znaleźć w dokumencie . Swoją drogą jestem autorem;)źródło
param_fixture
. Zobacz tę odpowiedź . Nie mogłem jednak znaleźć żadnego podobnego przykładu w dokumentach; Wiesz cokolwiek o tym?Zrobiłem zabawny dekorator, który pozwala pisać takie urządzenia:
Tutaj, po lewej stronie
/
masz inne urządzenia, a po prawej masz parametry, które są dostarczane za pomocą:Działa to w ten sam sposób, w jaki działają argumenty funkcji. Jeśli nie podasz
age
argumentu69
, zostanie użyty domyślny . jeśli nie dostarczyszname
lub pominieszdog.arguments
dekorator, otrzymasz zwykłyTypeError: dog() missing 1 required positional argument: 'name'
. Jeśli masz inne urządzenie, które przyjmuje argumentname
, nie powoduje to konfliktu z tym.Obsługiwane są również urządzenia asynchroniczne.
Dodatkowo daje to ładny plan konfiguracji:
Pełny przykład:
Kod dekoratora:
źródło