Atualização: uma vez que esta é a resposta aceita para esta pergunta e ainda recebe votos positivos às vezes, devo adicionar uma atualização. Embora a minha resposta original (abaixo) foi a única maneira de fazer isso em versões mais antigas do pytest como outros já observou pytest agora suporta parametrização indireta de acessórios. Por exemplo, você pode fazer algo assim (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
No entanto, embora essa forma de parametrização indireta seja explícita, como @Yukihiko Shinoda aponta, ela agora suporta uma forma de parametrização indireta implícita (embora eu não tenha conseguido encontrar nenhuma referência óbvia a isso nos documentos oficiais):
# 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
Não sei exatamente quais são as semânticas desse formulário, mas parece que pytest.mark.parametrize
reconhece que embora o test_tc1
método não receba um argumento chamado tester_arg
, o tester
acessório que está usando o faz, então ele passa o argumento parametrizado pelo tester
acessório.
Eu tive um problema semelhante - eu tenho um acessório chamado test_package
e, mais tarde, quis ser capaz de passar um argumento opcional para aquele acessório ao executá-lo em testes específicos. Por exemplo:
@pytest.fixture()
def test_package(request, version='1.0'):
...
request.addfinalizer(fin)
...
return package
(Não importa para esses propósitos o que o aparelho faz ou que tipo de objeto retornado package
) é.
Então, seria desejável usar de alguma forma esse acessório em uma função de teste de forma que eu também pudesse especificar o version
argumento daquele acessório para usar com esse teste. No momento, isso não é possível, embora possa ser um bom recurso.
Nesse ínterim, foi fácil fazer meu aparelho simplesmente retornar uma função que faz todo o trabalho que o aparelho fazia anteriormente, mas me permite especificar o 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
Agora posso usar isso em minha função de teste, como:
def test_install_package(test_package):
package = test_package(version='1.1')
...
assert ...
e assim por diante.
A tentativa de solução do OP estava indo na direção certa e, como sugere a resposta de @ hpk42 , ele MyTester.__init__
poderia simplesmente armazenar uma referência à solicitação 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"
Em seguida, use isso para implementar o acessório como:
@pytest.fixture()
def tester(request):
""" create tester object """
# how to use the list below for arg?
_tester = MyTester(request)
return _tester
Se desejado, a MyTester
classe pode ser reestruturada um pouco para que seu .args
atributo possa ser atualizado depois de ter sido criado, para ajustar o comportamento para testes individuais.
Na verdade, isso é suportado nativamente em py.test por meio de parametrização indireta .
No seu caso, você teria:
fonte
indirect
argumento da palavra - chave é reconhecidamente esparsa e hostil, o que provavelmente explica a obscuridade desta técnica essencial. Eu vasculhei o site py.test em várias ocasiões em busca desse recurso - apenas para aparecer vazio, mais antigo e confuso. A amargura é um lugar conhecido como integração contínua. Obrigado Odin pelo Stackoverflow.test_tc1
torna-setest_tc1[tester0]
.indirect=True
passa os parâmetros para todos os aparelhos chamados, certo? Porque a documentação nomeia explicitamente os aparelhos para parametrização indireta, por exemplo, para um aparelho chamadox
:indirect=['x']
Você pode acessar o módulo / classe / função solicitante de funções de fixture (e, portanto, de sua classe de testador), consulte interação com o contexto de teste de solicitação de uma função de fixture . Portanto, você pode declarar alguns parâmetros em uma classe ou módulo e o equipamento do testador pode pegá-lo.
fonte
@fixture def my_fixture(request)
e, em seguida,@pass_args(arg1=..., arg2=...) def test(my_fixture)
obter esses argumentosmy_fixture()
assimarg1 = request.arg1, arg2 = request.arg2
. Algo assim é possível em py.test agora?Não consegui encontrar nenhum documento, no entanto, parece funcionar na versão mais recente do pytest.
fonte
Nadège
foi revertido. Portanto, esse recurso não documentado (acho que ainda é ilegal?) Ainda vive.Para melhorar um pouco a resposta de imiric : outra forma elegante de resolver este problema é criar "luminárias de parâmetros". Eu pessoalmente prefiro isso em vez do
indirect
recurso depytest
. Este recurso está disponível empytest_cases
e a ideia original foi sugerida por Sup3rGeo .Observe que
pytest-cases
também fornece@pytest_fixture_plus
que permitem que você use marcas de parametrização em seus aparelhos de iluminação e@cases_data
que permitem que você forneça seus parâmetros de funções em um módulo separado. Veja doc para detalhes. A propósito, sou o autor;)fonte
param_fixture
. Veja esta resposta . Não consegui encontrar nenhum exemplo semelhante na documentação; Você sabe algo sobre isso?Fiz um decorador engraçado que permite escrever acessórios como este:
Aqui, à esquerda de
/
você tem outros acessórios, e à direita você tem parâmetros que são fornecidos usando:Isso funciona da mesma maneira que os argumentos de função. Se você não fornecer o
age
argumento, o padrão,,69
será usado. se você não fornecername
ou omitir odog.arguments
decorador, você receberá o regularTypeError: dog() missing 1 required positional argument: 'name'
. Se você tiver outro aparelho que requer argumentoname
, ele não entra em conflito com este.Dispositivos assíncronos também são suportados.
Além disso, isso oferece um bom plano de configuração:
Um exemplo completo:
O código para o decorador:
fonte