Problème de PATH avec pytest 'ImportError: Aucun module nommé YadaYadaYada'

231

J'ai utilisé easy_install pour installer pytest sur un mac et j'ai commencé à écrire des tests pour un projet avec une structure de fichiers comme ceci:

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

exécuter py.testdans le répertoire repo, tout se comporte comme vous vous en doutez

mais quand j'essaye la même chose sur Linux ou Windows (les deux ont pytest 2.2.3 sur eux), il aboie chaque fois qu'il frappe sa première importation de quelque chose de mon chemin d'application. Dites par exemplefrom app import some_def_in_app

Dois-je modifier mon CHEMIN pour exécuter py.test sur ces systèmes? Quelqu'un at-il vécu cela?

MattoTodd
la source
4
Voici le moyen de le corriger avec setuptools.
ederag
4
Veuillez vérifier la réponse @hoefling et envisager de changer celle que vous avez acceptée, si SO le permet après ce temps: beaucoup mieux!
Davide

Réponses:

91

Oui, le dossier source n'est pas dans le chemin de Python si vous accédez cdau répertoire tests.

Vous avez 2 choix:

  1. Ajoutez le chemin manuellement aux fichiers de test, quelque chose comme ceci:

    import sys, os
    myPath = os.path.dirname(os.path.abspath(__file__))
    sys.path.insert(0, myPath + '/../')
  2. Exécutez les tests avec la var env PYTHONPATH=../.

Not_a_Golfer
la source
11
quand suis-je cddans un répertoire? je cours py.testdepuis ma racine. sauf si je me trompe et que vous voulez dire que pytest marche dans mes dossiers
MattoTodd
si c'était un cdproblème, ne le toucherais-je pas également sur mac?
MattoTodd
Oh, j'ai mal lu et j'ai pensé que cela ne fonctionnait pas depuis le répertoire tests. encore l'astuce de la suggestion 1 fonctionnerait. J'utilise uniquement Linux, donc je ne peux pas expliquer le comportement sur d'autres systèmes d'exploitation.
Not_a_Golfer
avez-vous une importation comme ça sur tous vos fichiers test.py?
MattoTodd
4
oui, mais ma structure de répertoires est généralement légèrement différente - je garde généralement / src et / test sous le répertoire racine.
Not_a_Golfer
276

Je ne sais pas pourquoi py.test n'ajoute pas le répertoire courant dans le PYTHONPATH lui-même, mais voici une solution de contournement (à exécuter à partir de la racine de votre référentiel):

python -m pytest tests/

Cela fonctionne parce que Python ajoute le répertoire actuel dans le PYTHONPATH pour vous.

Apteryx
la source
2
Il nécessite de réécrire les importations relatives en absolues, si vous avez le code pour exécuter l'application pas au niveau, d'où vous exécutez la commande. Par exemple: project/test/all-my-testset project/src/app.pyet à cause de ce changement, il faut appeler app.pyindirectement en utilisant un __main__.pyfichier dans project/src, afin que l'on puisse utiliser l'appel python -m src. Des choses assez désordonnées pour autant que je sache.
Zelphir Kaltstahl
3
@Zelphir: L'utilisation d'importations absolues est une pratique recommandée. Habnabit a un bon article sur les meilleures pratiques d'emballage: blog.habnab.it/blog/2013/07/21/python-packages-and-you , et PEP8 dit que "les importations relatives implicites ne devraient jamais être utilisées et ont été supprimées en Python 3. " Voir: python.org/dev/peps/pep-0008 .
Apteryx
1
@Apteryx Vous voulez dire "absolu du projet", n'est-ce pas? Parce que des choses comme ça /home/user/dev/projectxyz/src ...seraient vraiment mauvaises et ne fonctionneraient pas sur d'autres machines dans la plupart des cas. Je pense que ce que je voulais dire, c'est que je dois toujours écrire la racine du projet dans son intégralité, même si un module se trouve dans le même dossier que le fichier exécuté. Je ne savais pas que c'était considéré comme la meilleure pratique, c'est donc une information utile, merci. Je suis d'accord avec la plupart des pep8, même si ce n'est toujours pas parfait.
Zelphir Kaltstahl
1
@Zelphir, oui, c'est ce que je voulais dire. Je crois que le terme importations absolues en Python fait toujours référence à "project-absolue". Voir: python.org/dev/peps/pep-0328/#rationale-for-absolute-imports . En fait, je suis presque sûr que vous ne pouvez pas importer à partir d'emplacements de chemins absolus aléatoires, au moins en utilisant le mécanisme "d'importation" par défaut.
Apteryx
4
J'ai ajouté des __init__.pytests qui ont résolu le problème. Maintenant, je peux utiliserpytest
Kiran Kumar Kotari
154

conftest Solution

La solution la moins invasive consiste à ajouter un fichier vide nommé conftest.pydans le repo/répertoire:

$ touch repo/conftest.py

C'est tout. Pas besoin d'écrire du code personnalisé pour manipuler le sys.pathou n'oubliez pas de le faire glisser PYTHONPATH, ou de le placer __init__.pydans des répertoires où il n'appartient pas.

Le répertoire du projet ensuite:

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

Explication

pytestrecherche les conftestmodules sur la collection de tests pour rassembler des crochets et des fixtures personnalisés, et afin d'en importer les objets personnalisés, pytestajoute le répertoire parent de conftest.pyàsys.path (dans ce cas, le reporépertoire).

Autres structures de projet

Si vous avez une autre structure de projet, placez le conftest.pydans le répertoire racine du package (celui qui contient les packages mais n'est pas un package lui-même, donc ne contient pas de __init__.py), par exemple:

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

src disposition

Bien que cette approche puisse être utilisée avec la srcmise en page (place conftest.pydans le srcrépertoire):

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

méfiez-vous que l'ajout srcpour PYTHONPATHatténuer le sens et les avantages de la srcmise en page! Vous finirez par tester le code du référentiel et non le package installé. Si vous devez le faire, vous n'avez peut-être pas du tout besoin du srcrépertoire.

Où aller en partant d'ici

Bien sûr, les conftestmodules ne sont pas seulement des fichiers pour faciliter la découverte du code source; c'est là que toutes les améliorations spécifiques au projet du pytestcadre et la personnalisation de votre suite de tests se produisent. pytesta beaucoup d'informations sur les conftestmodules dispersés dans leurs documents ; commencer par conftest.py: plugins locaux par répertoire

Aussi, SO a une excellente question sur les conftestmodules: dans py.test, à quoi sert les fichiers conftest.py?

hoefling
la source
2
@ aaa90210 bien que je ne puisse pas reproduire votre problème (l'importation à partir d'un conftest dans un répertoire racine fonctionne à n'importe quel niveau), vous ne devriez jamais importer à partir de fichiers conftest car c'est un nom réservé pytestet il est fortement déconseillé de le faire. En faisant cela, vous plantez des graines pour de futures erreurs. Créez un autre module nommé utils.pyet placez-y le code à réutiliser dans les tests.
hoefling
4
Pouces vers le haut! C'est la seule solution qui fonctionne bien pour moi.
130
4
devrait absolument être la réponse acceptée - merci!
Martin Peck
2
Logiquement, conftest.pyn'appartient pas au code d'application et, imo, le placer sous src/n'est pas correct.
Nik O'Lai
2
Cette réponse devrait être un en-tête fixe sur SO. Merci beaucoup.
Rigoberta Raviolini
119

J'ai eu le même problème. Je l'ai corrigé en ajoutant un __init__.pyfichier vide à mon testsrépertoire.

Aron Curzon
la source
77
Notez que ceci n'est PAS recommandé par 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
Je suis venu ici avec la même question et __init__.pyj'ai trouvé que la suppression de mon répertoire de tests l'avait résolu pour moi.
101
3
@mafro je ne vois pas le problème? Les tests n'ont pas besoin d'être du code importable, ils sont trouvés par votre lanceur de test. Seul le code à tester doit être un package / module installé, pas les tests.
K.-Michael Aye
5
L'ajout d'un __init__.pysous-répertoire dans test/rend le travail d'importation absolu pour l'exécution de tests spécifiques dans ce sous-répertoire par rapport aux modules à installer. Merci.
Bryce Guinta
7
voilà : doc.pytest.org/en/latest/goodpractices.html très facile à trouver avec google.
K.-Michael Aye
46

S'exécute pytesten tant que module avec: python -m pytest tests

Stefano Messina
la source
3
Cela semble être une solution de travail, mais quelqu'un peut-il expliquer POURQUOI? Je préfère corriger la cause sous-jacente plutôt que de l'utiliser python -m pytestsans autre explication que "parce que ça marche"
Janne Enberg
4
Cela se produit lorsque la hiérarchie du projet est par exemple: package/src package/testset que testsvous importez depuis src. L'exécution en tant que module considérera les importations comme absolues par rapport à l'emplacement d'exécution.
Stefano Messina
1
Cette solution m'a aidé, merci! La cause en était à cause du conflit dans la version Python. Le test pytest fonctionne pour la version antérieure de python. Dans ma situation, ma version python est 3.7.1, python -m pytest tests fonctionne mais pas pytest tests.
Ruxi Zhang
1
Extrait de Pytest "L'exécution de pytest avec python -m pytest [...] au lieu de pytest [...] donne un comportement presque équivalent, sauf que l'ancien appel ajoutera le répertoire courant à sys.path."
Moad Ennagi
37

Vous pouvez exécuter avec PYTHONPATH à la racine du projet

PYTHONPATH=. py.test

Ou utilisez l'installation de pip comme importation modifiable

pip install -e .   # install package using setup.py in editable mode
Ford Guo
la source
3
Cela n'a pas fonctionné pour moi avec un testrépertoire qui n'est pas dans la srcstructure du répertoire et qui appelle depuis le répertoire contenant à la fois le répertoire testet src.
Zelphir Kaltstahl
21

J'ai créé cela comme une réponse à votre question et à ma propre confusion. J'espère que ça aide. Faites attention à PYTHONPATH dans la ligne de commande py.test et dans le tox.ini.

https://github.com/jeffmacdonald/pytest_test

Plus précisément: vous devez indiquer à py.test et tox où trouver les modules que vous incluez.

Avec py.test, vous pouvez le faire:

PYTHONPATH=. py.test

Et avec tox, ajoutez ceci à votre tox.ini:

[testenv]
deps= -r{toxinidir}/requirements.txt
commands=py.test
setenv =
    PYTHONPATH = {toxinidir}
Jeff MacDonald
la source
1
Pourriez-vous donner une brève explication du projet que vous avez lié?
JF Meier
1
C'est peut-être juste moi, mais le LISEZMOI sur le projet est assez détaillé, et mon commentaire sur stackoverflow explique pourquoi j'ai créé le dépôt.
Jeff MacDonald
5
Bien que cela ne soit pas strictement nécessaire, il est habituel d'avoir le contenu principal d'une réponse dans la réponse elle-même, car cela garantit que la réponse est compréhensible dans x ans à partir de ce moment où la ressource liée peut avoir disparu depuis longtemps.
JF Meier
:) Tant pis. C'est Internet pour toi.
Jeff MacDonald
9

J'ai eu le même problème à Flask.

Quand j'ai ajouté:

__init__.py

dans le dossier des tests, le problème a disparu :)

L'application n'a probablement pas reconnu les tests de dossier comme module

user13037517
la source
8

Je l'ai corrigé en supprimant le niveau supérieur __init__.pydans le dossier parent de mes sources.

Gonzalo
la source
1
Fixé pour moi. Quelqu'un peut-il expliquer cela?
aboger
ici, il l'a aussi corrigé pour moi. aimerait vraiment voir une explication à cela si quelqu'un l'a
king_wayne
j'ai ajouté init .py mais faisais toujours face aux mêmes problèmes mais cette solution a fonctionné pour moi aussi .. raison s'il vous plaît?
Abhijit
7

J'ai commencé à obtenir des ConftestImportFailure: ImportError('No module named ...erreurs étranges lorsque j'ai accidentellement ajouté un __init__.pyfichier à mon répertoire src (qui n'était pas censé être un package Python, juste un conteneur de toutes les sources).

jbasko
la source
3

J'obtenais cette erreur en raison de quelque chose d'encore plus simple (on pourrait même dire trivial). Je n'avais pas installé le pytestmodule. Un simple apt install python-pytestproblème m'a donc été résolu.

'pytest' aurait été répertorié dans setup.py comme dépendance de test. Assurez-vous d'installer également les exigences de test.

craq
la source
3

J'ai eu un problème similaire. pytestn'a pas reconnu un module installé dans l'environnement dans lequel je travaillais.

Je l'ai résolu en installant également pytestdans le même environnement.

nocibambi
la source
Bien que j'utilisais pytest de l'intérieur d'un venv, je l'ai également fait installer globalement, ce qui m'a donné cette erreur. Après avoir désinstallé la version globale et installé à l'intérieur du venv, cela a fonctionné.
Markus Ressel
2

Pour moi, le problème a été tests.pygénéré par Django avec le testsrépertoire. La suppression a tests.pyrésolu le problème.

Paweł Mucha
la source
2

J'ai eu cette erreur car j'ai mal utilisé les importations relatives. Dans l'exemple OP, test_app.py devrait importer des fonctions en utilisant par exemple

from repo.app import *

Cependant, les fichiers __init__.py sont généreusement dispersés dans la structure de fichiers, cela ne fonctionne pas et crée le type d'ImportError vu à moins que les fichiers et les fichiers de test ne se trouvent dans le même répertoire.

from app import *

Voici un exemple de ce que j'avais à faire avec l'un de mes projets:

Voici ma structure de projet:

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

Pour pouvoir accéder à activity_indicator.py à partir de test_activity_indicator.py, je devais:

  • lancez test_activity_indicatory.py avec l'importation relative correcte:
    from microbit.activity_indicator.activity_indicator import *
  • placez les fichiers __init__.py dans toute la structure du projet:
    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
la source
0

Très souvent, les tests ont été interrompus en raison de l'impossibilité d'importer le module.Après la recherche, j'ai découvert que le système examine le fichier au mauvais endroit et nous pouvons facilement surmonter le problème en copiant le fichier contenant le module dans le même dossier que celui indiqué, afin d'être correctement importé. Une autre proposition de solution serait de changer la déclaration d'importation et de montrer à MutPy le chemin correct de l'unité. Cependant, étant donné que plusieurs unités peuvent avoir cette dépendance, ce qui signifie que nous devons également valider les modifications dans leurs déclarations, nous préférons simplement déplacer l'unité dans le dossier.

Baydaa
la source
Il y a d'autres réponses qui fournissent la question du PO, et elles ont été publiées il y a quelque temps. Lorsque vous publiez une réponse, assurez-vous d'ajouter soit une nouvelle solution, soit une meilleure explication, en particulier lorsque vous répondez à des questions plus anciennes. Parfois, il est préférable de poster un commentaire sur une réponse particulière.
help-info.de
En plus du commentaire de @ help-info.de: Voici le lien vers le guide pour répondre aux questions: stackoverflow.com/help/how-to-answer
la main de NOD
0

Selon un article sur Dirk Avery sur Medium (et soutenu par mon expérience personnelle), si vous utilisez un environnement virtuel pour votre projet, vous ne pouvez pas utiliser une installation de pytest à l'échelle du système; vous devez l'installer dans l'environnement virtuel et utiliser cette installation.

En particulier, si vous l'avez installé aux deux endroits, l'exécution de la pytestcommande ne fonctionnera pas, car elle utilisera l'installation du système. Comme les autres réponses l'ont décrit, une solution simple consiste à exécuter à la python -m pytestplace de pytest; cela fonctionne car il utilise la version de l'environnement de pytest. Alternativement, vous pouvez simplement désinstaller la version système de pytest; après avoir réactivé l'environnement virtuel, la pytestcommande devrait fonctionner.

Einhaender
la source
Jusqu'à présent, la seule chose qui a fonctionné pour moi était python -m pytest tests/.
Nik O'Lai
0

Je faisais le même problème en suivant le tutoriel Flask et j'ai trouvé la réponse sur les Pytest officielles docs Il est un peu passer de la façon dont je (et je pense que beaucoup d' autres) sont utilisées pour faire des choses.

Vous devez créer un setup.pyfichier dans le répertoire racine de votre projet avec au moins les deux lignes suivantes:

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

où PACKAGENAME est le nom de votre application. Ensuite, vous devez l'installer avec pip:

pip install -e .

L' -eindicateur indique à pip d'intaller le paquetage en mode modifiable ou "développement". Donc, la prochaine fois que vous l'exécutez, pytestvotre application devrait trouver la norme PYTHONPATH.

Luis Lezcano Airaldi
la source
0

Ma solution:

créez le conftest.pyfichier dans le testrépertoire contenant:

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

Cela ajoutera le dossier d'intérêt au chemin python sans modifier chaque fichier de test , définir la variable env ou jouer avec les chemins absolus / relatifs.

davide burba
la source