Actualización: dado que esta es la respuesta aceptada a esta pregunta y a veces todavía se vota a favor, debería agregar una actualización. Aunque mi respuesta original (a continuación) era la única forma de hacer esto en versiones anteriores de pytest, ya que otros han notado que pytest ahora admite la parametrización indirecta de accesorios. Por ejemplo, puede hacer algo como esto (a través de @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
Sin embargo, aunque esta forma de parametrización indirecta es explícita, como @Yukihiko Shinoda señala que ahora es compatible con una forma de parametrización indirecta implícita (aunque no pude encontrar ninguna referencia obvia a esto en los documentos oficiales):
# 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
No sé exactamente cuál es la semántica de esta forma, pero parece que pytest.mark.parametrize
reconoce que aunque el test_tc1
método no toma un argumento con nombre tester_arg
, el tester
dispositivo que está usando lo hace, por lo que pasa el argumento parametrizado a través del tester
dispositivo.
Tuve un problema similar: tengo un dispositivo llamado test_package
, y luego quería poder pasar un argumento opcional a ese dispositivo cuando lo ejecutaba en pruebas específicas. Por ejemplo:
@pytest.fixture()
def test_package(request, version='1.0'):
...
request.addfinalizer(fin)
...
return package
(No importa para estos propósitos qué hace el dispositivo o qué tipo de objeto devuelve package
).
Entonces sería deseable usar de alguna manera este accesorio en una función de prueba de tal manera que también pueda especificar el version
argumento de ese accesorio para usar con esa prueba. Actualmente, esto no es posible, aunque podría ser una buena característica.
Mientras tanto, fue bastante fácil hacer que mi dispositivo simplemente devuelva una función que hace todo el trabajo que hizo el dispositivo anteriormente, pero me permite especificar el version
argumento:
@pytest.fixture()
def test_package(request):
def make_test_package(version='1.0'):
...
request.addfinalizer(fin)
...
return test_package
return make_test_package
Ahora puedo usar esto en mi función de prueba como:
def test_install_package(test_package):
package = test_package(version='1.1')
...
assert ...
y así.
El intento de solución del OP se dirigió en la dirección correcta y, como sugiere la respuesta de @ hpk42 , MyTester.__init__
podría simplemente almacenar una referencia a la solicitud como:
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"
Luego use esto para implementar el accesorio como:
@pytest.fixture()
def tester(request):
""" create tester object """
# how to use the list below for arg?
_tester = MyTester(request)
return _tester
Si lo desea, la MyTester
clase podría reestructurarse un poco para que su .args
atributo se pueda actualizar después de que se haya creado, para modificar el comportamiento de las pruebas individuales.