Problema PATH com pytest 'ImportError: nenhum módulo chamado YadaYadaYada'

231

Eu usei o easy_install para instalar o pytest em um mac e comecei a escrever testes para um projeto com uma estrutura de arquivos como esta:

repo/
repo/app.py
repo/settings.py
repo/models.py
repo/tests/
repo/tests/test_app.py

executar py.testenquanto no diretório repo, tudo se comporta como você esperaria

mas quando tento a mesma coisa no linux ou no windows (ambos possuem o pytest 2.2.3), ele late sempre que atinge sua primeira importação de algo do caminho do aplicativo. Digamos, por exemplofrom app import some_def_in_app

Preciso editar meu PATH para executar o py.test nesses sistemas? Alguém já passou por isso?

MattoTodd
fonte
4
Aqui está a maneira de corrigi-lo com o setuptools.
Ederag # 14/16
4
Por favor, verifique a resposta @hoefling e considere alterar a que você aceita, se o SO permitir depois de tanto tempo: muito melhor!
Davide

Respostas:

91

Sim, a pasta de origem não está no caminho do Python se você estiver cdno diretório de testes.

Você tem 2 opções:

  1. Adicione o caminho manualmente aos arquivos de teste, algo como isto:

    import sys, os
    myPath = os.path.dirname(os.path.abspath(__file__))
    sys.path.insert(0, myPath + '/../')
  2. Execute os testes com o env var PYTHONPATH=../.

Not_a_Golfer
fonte
11
quando estou cdindo para um diretório? Eu estou correndo py.testda minha raiz. se não me engano e você quer dizer como pytest anda através de minhas pastas
MattoTodd
se fosse um cdproblema, eu não iria acertá-lo também no mac?
precisa saber é o seguinte
Ah, eu interpretei errado e pensei que não funcionaria no diretório de testes. ainda o truque na sugestão 1 funcionaria. Eu só uso Linux, então não consigo explicar o comportamento em outros sistemas operacionais.
precisa saber é o seguinte
você tem uma importação como essa em todos os seus arquivos test.py?
precisa saber é o seguinte
4
sim, mas minha estrutura de diretórios geralmente é um pouco diferente - geralmente mantenho / src e / test no diretório raiz.
precisa saber é o seguinte
275

Não sei por que o py.test não adiciona o diretório atual no próprio PYTHONPATH, mas aqui está uma solução alternativa (a ser executada a partir da raiz do seu repositório):

python -m pytest tests/

Funciona porque o Python adiciona o diretório atual no PYTHONPATH para você.

Apteryx
fonte
2
É necessário reescrever as importações relativas às absolutas, se você tiver o código para executar o aplicativo que não está no nível, de onde você executa o comando. Por exemplo: project/test/all-my-testse project/src/app.pypor causa dessa alteração, é necessário chamar o app.pyindiretamente usando um __main__.pyarquivo project/src, para que você possa usar a chamada python -m src. Coisas bem bagunçadas, até onde eu sei.
Zelphir Kaltstahl 26/09
3
@ Zelphir: Usar importações absolutas é uma prática recomendada. O Habnabit's tem um bom artigo sobre as melhores práticas de empacotamento: blog.habnab.it/blog/2013/07/21/python-packages-and-you , e o PEP8 diz que "importações relativas implícitas nunca devem ser usadas e foram removidas no Python 3. " Veja: python.org/dev/peps/pep-0008 .
Apteryx
1
@Apteryx Você quer dizer "projeto absoluto", certo? Porque coisas assim /home/user/dev/projectxyz/src ...seriam muito ruins e não seriam executadas em outras máquinas na maioria dos casos. Eu acho que o que eu quis dizer é que eu sempre devo escrever a raiz do projeto inteiro no caminho do módulo, mesmo que um módulo esteja na mesma pasta que o arquivo executado. Eu não sabia que isso é considerado uma prática recomendada, por isso é uma informação útil, obrigado. Eu concordo com a maioria do pep8, embora ainda não seja perfeito.
Zelphir Kaltstahl 6/11
1
@ Zelphir, sim, foi o que eu quis dizer. Acredito que o termo importações absolutas em Python sempre se refira a "projeto-absoluto". Veja: python.org/dev/peps/pep-0328/#rationale-for-absolute-imports . Na verdade, tenho certeza de que você não pode importar de locais aleatórios e absolutos de caminhos, pelo menos usando o mecanismo "importar" padrão.
Apteryx
4
Eu adicionei __init__.pyem testes que resolveram o problema. Agora eu posso usarpytest
Kiran Kumar Kotari
154

conftest solução

A solução menos invasiva é adicionar um arquivo vazio nomeado conftest.pyno repo/diretório:

$ touch repo/conftest.py

É isso aí. Não há necessidade de escrever um código personalizado para manipular sys.pathou lembrar-se de arrastar PYTHONPATHou colocar __init__.pyem diretórios onde não pertence.

O diretório do projeto depois:

repo
├── conftest.py
├── app.py
├── settings.py
├── models.py
└── tests
     └── test_app.py

Explicação

pytestprocura os conftestmódulos na coleção de testes para reunir ganchos e acessórios personalizados e, para importar os objetos personalizados deles, pytestadiciona o diretório pai conftest.pydosys.path (no caso, o repodiretório).

Outras estruturas do projeto

Se você tiver outra estrutura de projeto, coloque o conftest.pydiretório raiz do pacote (aquele que contém pacotes, mas não é um pacote em si, portanto não contém um __init__.py), por exemplo:

repo
├── conftest.py
├── spam
   ├── __init__.py
   ├── bacon.py
   └── egg.py
├── eggs
   ├── __init__.py
   └── sausage.py
└── tests
     ├── test_bacon.py
     └── test_egg.py

src disposição

Embora essa abordagem possa ser usada com o srclayout (coloque conftest.pyno srcdir):

repo
├── src
   ├── conftest.py
   ├── spam
      ├── __init__.py
      ├── bacon.py
      └── egg.py
   └── eggs 
       ├── __init__.py
       └── sausage.py
└── tests
     ├── test_bacon.py
     └── test_egg.py

cuidado que adicionar srcao PYTHONPATHatenua o significado e os benefícios do srclayout! Você terminará testando o código do repositório e não do pacote instalado. Se você precisar fazer isso, talvez não precise do srcdir.

Para onde ir daqui

Obviamente, os conftestmódulos não são apenas alguns arquivos para ajudar na descoberta do código-fonte; é onde todos os aprimoramentos específicos do projeto da pytestestrutura e a personalização do seu conjunto de testes acontecem. pytesttem muitas informações sobre os conftestmódulos espalhados pelos documentos ; comece com conftest.py: plugins locais por diretório

Além disso, o SO tem uma excelente pergunta sobre os conftestmódulos: No py.test, qual é a utilidade dos arquivos conftest.py?

hoefling
fonte
2
@ aaa90210 embora eu não possa reproduzir seu problema (a importação de um conftest em um diretório raiz funciona em qualquer nível), você nunca deve importar de arquivos conftest, pois é um nome reservado pyteste é altamente recomendável que não o faça. Ao fazer isso, você planta sementes para erros futuros. Crie outro módulo chamado utils.pye coloque o código para reutilização nos testes lá.
hoefling
4
Afirmativo! Esta é a única solução que funciona bem para mim.
130nrd 23/05/19
4
deve ser absolutamente a resposta aceita - obrigado!
Martin Peck
2
Logicamente, conftest.pynão pertence ao código do aplicativo e, imo, colocá-lo sob src/não está correto.
Nik O'Lai 27/04
2
Esta resposta deve ser um cabeçalho fixo no SO. Muito obrigado.
Rigoberta Raviolini
119

Eu tive o mesmo problema. Corrigi-o adicionando um __init__.pyarquivo vazio ao meu testsdiretório.

Aron Curzon
fonte
77
Note que este é não recomendado por py.test: avoid “__init__.py” files in your test directories. This way your tests can run easily against an installed version of mypkg, independently from the installed package if it contains the tests or not.SRC: pytest.org/latest/goodpractises.html
K.-Michael Aye
24
Eu vim aqui com a mesma pergunta e achei que a remoção __init__.pydo meu diretório de testes resolveu isso para mim.
101 de
3
@ mafro Não vejo o problema? Os testes não precisam ser um código importável, eles são encontrados pelo seu executor de testes. Somente o código a ser testado deve ser um pacote / módulo instalado, não os testes.
K.-Michael Aye
5
A adição de um __init__.pynos subdiretórios test/faz com que a importação absoluta funcione para a execução de testes específicos nesse subdiretório nos módulos a serem instalados. Obrigado.
precisa saber é o seguinte
7
lá está: doc.pytest.org/en/latest/goodpractices.html realmente fácil de encontrar com o google.
K.-Michael Aye
46

Execute- pytestse como um módulo com: python -m pytest tests

Stefano Messina
fonte
3
Esta parece ser uma solução funcional, mas alguém pode explicar POR QUE? Eu prefiro corrigir a causa subjacente do que o uso apenas python -m pytestsem qualquer explicação que não seja "porque funciona"
Janne Enberg
4
Isso acontece quando a hierarquia do projeto é, por exemplo: package/src package/testse na testsimportação de src. A execução como um módulo considerará as importações como absolutas que em relação ao local de execução.
Stefano Messina
1
Esta solução me ajudou, obrigado! A causa disso foi por causa do conflito na versão Python. O teste pytest funciona para a versão anterior do python. Na minha situação, minha versão python é 3.7.1, python -m pytest tests funciona, mas não testes pytest.
Ruxi Zhang
1
Do Pytest "Executar o pytest com python -m pytest em [...] vez de pytest produz um comportamento quase equivalente, exceto que a chamada anterior adicionará o diretório atual ao sys.path."
Moad Ennagi 04/04/19
37

Você pode executar com PYTHONPATH na raiz do projeto

PYTHONPATH=. py.test

Ou use a instalação do pip como importação editável

pip install -e .   # install package using setup.py in editable mode
Ford Guo
fonte
3
Isso não funcionou para mim com um testdiretório que não está na srcestrutura de diretórios e que chamou do diretório que contém os dois teste o srcdiretório.
Zelphir Kaltstahl
21

Eu criei isso como uma resposta para sua pergunta e minha própria confusão. Espero que ajude. Preste atenção ao PYTHONPATH na linha de comando py.test e no tox.ini.

https://github.com/jeffmacdonald/pytest_test

Especificamente: Você deve informar ao py.test e tox onde encontrar os módulos que você está incluindo.

Com py.test, você pode fazer isso:

PYTHONPATH=. py.test

E com o tox, adicione isso ao seu tox.ini:

[testenv]
deps= -r{toxinidir}/requirements.txt
commands=py.test
setenv =
    PYTHONPATH = {toxinidir}
Jeff MacDonald
fonte
1
Você poderia dar uma breve explicação para o projeto que vinculou?
JF Meier
1
Talvez seja só eu, mas o README do projeto é bastante detalhado, e meu comentário no stackoverflow diz por que criei o repositório.
101316 Jeff MacDonald
5
Embora não seja estritamente necessário, é uma política comum ter o conteúdo principal de uma resposta na própria resposta, pois garante que a resposta seja compreensível dentro de x anos, a partir de agora, quando o recurso vinculado estiver ausente.
JF Meier
:) Ah bem. Essa é a internet para você.
101316 Jeff MacDonald
9

Eu tive o mesmo problema no Flask.

Quando adicionei:

__init__.py

para pasta de testes, problema desapareceu :)

Provavelmente o aplicativo não conseguiu reconhecer os testes de pasta como módulo

user13037517
fonte
8

Corrigi-o removendo o nível superior __init__.pyna pasta pai das minhas fontes.

Gonzalo
fonte
1
Corrigido para mim. Alguém pode explicar isso?
aboger
isso aqui também corrigiu para mim. definitivamente adoraria ver uma explicação para isso, se alguém tem que
king_wayne
Eu adicionei o init .py, mas ainda estava enfrentando os mesmos problemas, mas esta solução funcionou para mim também .. razão, por favor?
Abhijit
7

Comecei a receber ConftestImportFailure: ImportError('No module named ...erros estranhos quando adicionei acidentalmente um __init__.pyarquivo ao meu diretório src (que não deveria ser um pacote Python, apenas um contêiner de todas as fontes).

jbasko
fonte
3

Eu estava recebendo esse erro devido a algo ainda mais simples (você pode até dizer trivial). Eu não tinha instalado o pytestmódulo. Então, um simples apt install python-pytestcorrigiu isso para mim.

'pytest' teria sido listado em setup.py como uma dependência de teste. Certifique-se de instalar os requisitos de teste também.

craq
fonte
3

Eu tive uma questão semelhante. pytestnão reconheceu um módulo instalado no ambiente em que estava trabalhando.

Eu o resolvi instalando também pytestno mesmo ambiente.

nocibambi
fonte
Embora eu estivesse usando pytest de dentro de um venv, também o instalei globalmente, o que me deu esse erro. Depois de desinstalar a versão global e instalar dentro do venv, funcionou.
Markus Ressel 10/03
2

Para mim, o problema foi tests.pygerado pelo Django junto com o testsdiretório. A remoção tests.pyresolveu o problema.

Paweł Mucha
fonte
2

Eu recebi esse erro porque usei importações relativas incorretamente. No exemplo do OP, test_app.py deve importar funções usando, por exemplo,

from repo.app import *

No entanto, os arquivos __init__.py liberalmente estão espalhados pela estrutura do arquivo, isso não funciona e cria o tipo de ImportError visto, a menos que os arquivos e os arquivos de teste estejam no mesmo diretório.

from app import *

Aqui está um exemplo do que eu tive que fazer com um dos meus projetos:

Aqui está a estrutura do meu projeto:

microbit/
microbit/activity_indicator/activity_indicator.py
microbit/tests/test_activity_indicator.py

Para acessar o activity_indicator.py em test_activity_indicator.py, eu precisava:

  • inicie test_activity_indicatory.py com a importação relativa correta:
    from microbit.activity_indicator.activity_indicator import *
  • coloque arquivos __init__.py em toda a estrutura do projeto:
    microbit/
    microbit/__init__.py
    microbit/activity_indicator/__init__.py
    microbit/activity_indicator/activity_indicator.py
    microbit/tests/__init__.py
    microbit/tests/test_activity_indicator.py
Oppy
fonte
0

Muitas vezes, os testes eram interrompidos devido à impossibilidade de importação do módulo. Após a pesquisa, descobri que o sistema está olhando o arquivo no lugar errado e podemos solucionar facilmente o problema, copiando o arquivo, contendo o módulo, em a mesma pasta indicada, para ser importada corretamente. Outra proposta de solução seria alterar a declaração para a importação e mostrar ao MutPy o caminho correto da unidade. No entanto, devido ao fato de que várias unidades podem ter essa dependência, o que significa que precisamos confirmar alterações também em suas declarações, preferimos simplesmente mover a unidade para a pasta.

Baydaa
fonte
Existem outras respostas que fornecem a pergunta do OP e foram publicadas há algum tempo. Ao postar uma resposta, adicione uma nova solução ou uma explicação substancialmente melhor, especialmente ao responder a perguntas mais antigas. Às vezes, é melhor postar um comentário em uma resposta específica.
help-info.de
Como complemento ao comentário de @ help-info.de: Aqui está o link para o guia para responder perguntas: stackoverflow.com/help/how-to-answer
a mão do NOD
0

De acordo com um post no Medium de Dirk Avery (e suportado pela minha experiência pessoal) se você estiver usando um ambiente virtual para o seu projeto, não poderá usar uma instalação do pytest em todo o sistema; você precisa instalá-lo no ambiente virtual e usar essa instalação.

Em particular, se você o tiver instalado nos dois lugares, simplesmente executar o pytestcomando não funcionará porque ele estará usando a instalação do sistema. Como as outras respostas descreveram, uma solução simples é executar em python -m pytestvez de pytest; isso funciona porque usa a versão do ambiente do pytest. Como alternativa, você pode simplesmente desinstalar a versão do sistema do pytest; após reativar o ambiente virtual, o pytestcomando deve funcionar.

Einhaender
fonte
A única coisa que funcionou para mim até agora foi python -m pytest tests/.
Nik O'Lai 27/04
0

Eu estava tendo o mesmo problema ao seguir o tutorial do Flask e encontrei a resposta nos documentos oficiais do Pytest. É uma pequena mudança na maneira como eu (e acho que muitos outros) costumamos fazer as coisas.

Você precisa criar um setup.pyarquivo no diretório raiz do seu projeto com pelo menos as duas linhas a seguir:

from setuptools import setup, find_packages
setup(name="PACKAGENAME", packages=find_packages())

onde PACKAGENAME é o nome do seu aplicativo. Então você precisa instalá-lo com o pip:

pip install -e .

O -esinalizador diz ao pip para instalar o pacote no modo editável ou "desenvolvido". Portanto, da próxima vez que você executá- pytestlo, seu aplicativo deverá estar no padrão PYTHONPATH.

Luis Lezcano Airaldi
fonte
0

Minha solução:

crie o conftest.pyarquivo no testdiretório que contém:

import os
import sys
sys.path.insert(0,os.path.dirname(os.path.realpath(__file__)) + "/relative/path/to/code/")

Isso adicionará a pasta de interesse ao caminho do python sem modificar todos os arquivos de teste , configurando a variável env ou alterando os caminhos absolutos / relativos.

Davide Burba
fonte