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.
En realidad, esto se admite de forma nativa en py.test mediante parametrización indirecta .
En tu caso, tendrías:
fuente
indirect
argumento de la palabra clave es ciertamente escasa y poco amigable, lo que probablemente explica la oscuridad de esta técnica esencial. He rastreado el sitio py.test en múltiples ocasiones en busca de esta misma característica, solo para aparecer vacío, viejo y aturdido. La amargura es un lugar conocido como integración continua. Gracias a Odin por Stackoverflow.test_tc1
se conviertetest_tc1[tester0]
.indirect=True
entrega los parámetros a todos los dispositivos llamados, ¿verdad? Porque la documentación nombra explícitamente los dispositivos para la parametrización indirecta, por ejemplo, para un dispositivo llamadox
:indirect=['x']
Puede acceder al módulo / clase / función de solicitud desde las funciones de dispositivo (y, por lo tanto, desde su clase Tester), consulte la interacción con la solicitud de contexto de prueba desde una función de dispositivo . Por lo tanto, puede declarar algunos parámetros en una clase o módulo y el dispositivo de prueba puede recogerlos.
fuente
@fixture def my_fixture(request)
y luego@pass_args(arg1=..., arg2=...) def test(my_fixture)
obtener estos argumentos demy_fixture()
esta maneraarg1 = request.arg1, arg2 = request.arg2
. ¿Es posible algo como esto en py.test ahora?No pude encontrar ningún documento, sin embargo, parece funcionar en la última versión de pytest.
fuente
Nadège
fue revertido. Por lo tanto, esta característica indocumentada (¿creo que todavía está indocumentada?) Aún vive.Para mejorar un poco la respuesta de imiric : otra forma elegante de resolver este problema es crear "accesorios de parámetros". Personalmente lo prefiero a la
indirect
función depytest
. Esta función está disponible enpytest_cases
, y la idea original fue sugerida por Sup3rGeo .Tenga en cuenta que
pytest-cases
también proporciona@pytest_fixture_plus
que le permiten usar marcas de parametrización en sus dispositivos, y@cases_data
que le permiten obtener sus parámetros de funciones en un módulo separado. Consulte el documento para obtener más detalles. Por cierto soy el autor;)fuente
param_fixture
. Vea esta respuesta . Sin embargo, no pude encontrar ningún ejemplo como este en los documentos; ¿Sabes algo sobre esto?Hice un decorador divertido que permite escribir accesorios como este:
Aquí, a la izquierda
/
tiene otros dispositivos, y a la derecha tiene parámetros que se suministran mediante:Esto funciona de la misma manera que funcionan los argumentos de función. Si no proporciona el
age
argumento, en su69
lugar se utiliza el predeterminado . si no proporcionaname
u omite eldog.arguments
decorador, obtiene el habitualTypeError: dog() missing 1 required positional argument: 'name'
. Si tiene otro accesorio que tiene discusiónname
, no entra en conflicto con este.También se admiten accesorios asíncronos.
Además, esto le brinda un buen plan de instalación:
Un ejemplo completo:
El código del decorador:
fuente