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?


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=../.


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.


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?


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.


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


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

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}

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


8

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


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).


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.


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.


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.


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

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.


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.


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.


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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.