No pytest, qual é a utilidade dos arquivos conftest.py?

218

Eu descobri recentemente pytest. Parece ótimo. No entanto, acho que a documentação poderia ser melhor.

Estou tentando entender para que conftest.pyarquivos devem ser usados.

No meu (atualmente pequeno) conjunto de testes, tenho um conftest.pyarquivo na raiz do projeto. Eu o uso para definir os equipamentos que eu injeto nos meus testes.

Eu tenho duas perguntas:

  1. É este o uso correto de conftest.py? Tem outros usos?
  2. Posso ter mais de um conftest.pyarquivo? Quando eu quero fazer isso? Exemplos serão apreciados.

De maneira mais geral, como você definiria a finalidade e o uso correto de conftest.pyarquivos em um conjunto de testes py.test?

Aviv Cohn
fonte
9
Você me teve emIt seems great. However, I feel the documentation could be better.
user9074332 08/08/19
7
Sim, a documentação poderia ser muito melhor. Pesquisei toda a documentação do pytest conftest.pye, embora existam muitas referências a esse procedimento ou a um arquivo conftest, em nenhum lugar da documentação ele indica que, quando o pytest faz o teste de descoberta, todos os arquivos conftest.py são encontrados (dentro do arquivo A estrutura do diretório sobre a qual a descoberta de teste está sendo executada) será executada durante a fase de coleta de testes (antes da execução de quaisquer testes). Tinha que descobrir isso sozinho através da experimentação.
Daniel Goldfarb

Respostas:

290

Esse é o uso correto do conftest.py?

Sim, ele é. As luminárias são um uso comum e potencial conftest.py. Os equipamentos que você definirá serão compartilhados entre todos os testes em seu conjunto de testes. No entanto, a definição de acessórios na raiz conftest.pypode ser inútil e atrasaria o teste se esses acessórios não fossem usados ​​em todos os testes.

Tem outros usos?

Sim.

  • Acessórios : defina acessórios para dados estáticos usados ​​por testes. Esses dados podem ser acessados ​​por todos os testes no conjunto, a menos que especificado de outra forma. Podem ser dados e auxiliares dos módulos que serão passados ​​a todos os testes.

  • Carregamento de plug-in externo : conftest.pyé usado para importar plug-ins ou módulos externos. Ao definir a seguinte variável global, o pytest carregará o módulo e o disponibilizará para seu teste. Plugins geralmente são arquivos definidos em seu projeto ou outros módulos que podem ser necessários em seus testes. Você também pode carregar um conjunto de plugins predefinidos, conforme explicado aqui .

    pytest_plugins = "someapp.someplugin"

  • Ganchos : você pode especificar ganchos como métodos de instalação e desmontagem e muito mais para melhorar seus testes. Para um conjunto de ganchos disponíveis, leia aqui . Exemplo:

    def pytest_runtest_setup(item):
         """ called before ``pytest_runtest_call(item). """
         #do some stuff`
  • Caminho da raiz de teste : esse é um recurso oculto. Ao definir conftest.pyseu caminho raiz, você terá que pytestreconhecer seus módulos de aplicativos sem especificar PYTHONPATH. Em segundo plano, py.test modifica o seu sys.pathincluindo todos os submódulos encontrados no caminho raiz.

Posso ter mais de um arquivo conftest.py?

Sim, você pode e é altamente recomendável que sua estrutura de teste seja um pouco complexa. conftest.pyarquivos têm escopo de diretório. Portanto, a criação de equipamentos e auxiliares direcionados é uma boa prática.

Quando eu quero fazer isso? Exemplos serão apreciados.

Vários casos podem caber:

Criando um conjunto de ferramentas ou ganchos para um grupo específico de testes.

root / mod / conftest.py

def pytest_runtest_setup(item):
    print("I am mod")
    #do some stuff


test root/mod2/test.py will NOT produce "I am mod"

Carregando um conjunto de acessórios para alguns testes, mas não para outros.

root / mod / conftest.py

@pytest.fixture()
def fixture():
    return "some stuff"

root / mod2 / conftest.py

@pytest.fixture()
def fixture():
    return "some other stuff"

root / mod2 / test.py

def test(fixture):
    print(fixture)

Irá imprimir "algumas outras coisas".

Substituindo ganchos herdados da raiz conftest.py.

root / mod / conftest.py

def pytest_runtest_setup(item):
    print("I am mod")
    #do some stuff

root / conftest.py

def pytest_runtest_setup(item):
    print("I am root")
    #do some stuff

Ao executar qualquer teste interno root/mod, apenas "I am mod" é impresso.

Você pode ler mais sobre conftest.py aqui .

EDITAR:

E se eu precisar que funções auxiliares simples sejam chamadas de vários testes em módulos diferentes - elas estarão disponíveis para mim se eu as colocar em um conftest.py? Ou devo simplesmente colocá-los em um módulo helpers.py e importá-los e usá-los em meus módulos de teste?

Você pode usar conftest.pypara definir seus ajudantes. No entanto, você deve seguir a prática comum. Os ajudantes podem ser usados ​​como acessórios pelo menos em pytest. Por exemplo, em meus testes, tenho um auxiliar redis falsos que injeto nos meus testes dessa maneira.

root / helper / redis / redis.py

@pytest.fixture
def mock_redis():
    return MockRedis()

root / tests / stuff / conftest.py

pytest_plugin="helper.redis.redis"

root / tests / stuff / test.py

def test(mock_redis):
    print(mock_redis.get('stuff'))

Este será um módulo de teste que você pode importar livremente em seus testes. NOTE que você poderia nomear redis.pycomo conftest.pyse seu módulo rediscontivesse mais testes. No entanto, essa prática é desencorajada por causa da ambiguidade.

Se você quiser usar conftest.py, basta colocar esse auxiliar em sua raiz conftest.pye injetá-lo quando necessário.

root / tests / conftest.py

@pytest.fixture
def mock_redis():
    return MockRedis()

root / tests / stuff / test.py

def test(mock_redis):
    print(mock_redis.get(stuff))

Outra coisa que você pode fazer é escrever um plugin instalável. Nesse caso, seu auxiliar pode ser gravado em qualquer lugar, mas ele precisa definir um ponto de entrada a ser instalado em sua e em outras estruturas de teste em potencial. Veja isso .

Se você não quiser usar acessórios, é claro que você pode definir um auxiliar simples e usar a importação antiga simples sempre que necessário.

root / tests / helper / redis.py

class MockRedis():
    # stuff

root / tests / stuff / test.py

from helper.redis import MockRedis

def test():
    print(MockRedis().get(stuff))

No entanto, aqui você pode ter problemas com o caminho, pois o módulo não está em uma pasta filho do teste. Você deve conseguir superar isso (não testado) adicionando um __init__.pyao seu auxiliar

root / tests / helper / __ init__.py

from .redis import MockRedis

Ou simplesmente adicionando o módulo auxiliar ao seu PYTHONPATH.

Simone Zandara
fonte
Estou encontrando uma situação em que meu primeiro módulo de teste de unidade test_aaaaa.pyestá realmente tentando executar antes que a instalação do equipamento seja concluída conftest.py. Alguma idéia de por que isso pode estar ocorrendo?
user9074332
Sem o conftest.py, o caminho raiz do teste não é adicionado ao sys.path e você verá erros como "ModuleNotFoundError: nenhum módulo chamado 'foobar'".
Cale Sweeney
11

Em um sentido amplo, conftest.py é um plug-in local por diretório. Aqui você define ganchos e acessórios específicos do diretório. No meu caso, tenho um diretório raiz contendo diretórios de testes específicos do projeto. Alguma mágica comum está estacionada em 'root' conftest.py. Projeto específico - em seus próprios. Não consigo ver nada de ruim no armazenamento de acessórios no conftest.py, a menos que não sejam amplamente utilizados (nesse caso, prefiro defini-los diretamente nos arquivos de teste)

Pato Furioso
fonte
10

Eu uso o conftest.pyarquivo para definir os equipamentos que injeto nos meus testes. Esse é o uso correto de conftest.py?

Sim , geralmente é usado um equipamento para preparar os dados para vários testes.

Tem outros usos?

Sim , um equipamento é uma função executada pytestantes e, às vezes, depois das funções de teste reais. O código no dispositivo elétrico pode fazer o que você quiser. Por exemplo, um acessório pode ser usado para obter um conjunto de dados para os testes trabalharem, ou um acessório também pode ser usado para colocar um sistema em um estado conhecido antes de executar um teste.

Posso ter mais de um conftest.pyarquivo? Quando eu quero fazer isso?

Primeiro, é possível colocar acessórios em arquivos de teste individuais. No entanto, para compartilhar acessórios entre vários arquivos de teste, você precisa usar um conftest.pyarquivo em algum lugar centralmente localizado para todos os testes. Os equipamentos podem ser compartilhados por qualquer teste. Eles podem ser colocados em arquivos de teste individuais, se você desejar que o equipamento seja usado apenas por testes nesse arquivo.

Segundo, sim , você pode ter outros conftest.pyarquivos nos subdiretórios do diretório de testes superior. Se o fizer, os equipamentos definidos nesses conftest.pyarquivos de nível inferior estarão disponíveis para testes nesse diretório e subdiretórios.

Por fim, colocar os acessórios no conftest.pyarquivo na raiz do teste os disponibilizará em todos os arquivos de teste.

lmiguelvargasf
fonte