PATH-Problem mit pytest 'ImportError: Kein Modul namens YadaYadaYada'

231

Ich habe easy_install verwendet, um pytest auf einem Mac zu installieren, und habe angefangen, Tests für ein Projekt mit einer Dateistruktur wie folgt zu schreiben:

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

Wenn Sie py.testim Repo-Verzeichnis ausgeführt werden, verhält sich alles wie erwartet

Aber wenn ich dasselbe unter Linux oder Windows versuche (beide haben Pytest 2.2.3), bellt es immer dann, wenn es zum ersten Mal etwas aus meinem Anwendungspfad importiert. Sagen Sie zum Beispielfrom app import some_def_in_app

Muss ich meinen PFAD bearbeiten, um py.test auf diesen Systemen auszuführen? Hat das jemand erlebt?

MattoTodd
quelle
4
Hier ist der Weg, um es mit Setuptools zu beheben.
ederag
4
Bitte überprüfen Sie die Antwort von @hoefling und überlegen Sie, Ihre akzeptierte zu ändern, wenn SO dies nach dieser langen Zeit zulässt: viel besser!
Davide

Antworten:

91

Ja, der Quellordner befindet sich nicht im Python-Pfad, wenn Sie cdzum Testverzeichnis wechseln .

Sie haben 2 Möglichkeiten:

  1. Fügen Sie den Pfad manuell zu den Testdateien hinzu.

    import sys, os
    myPath = os.path.dirname(os.path.abspath(__file__))
    sys.path.insert(0, myPath + '/../')
  2. Führen Sie die Tests mit der env var aus PYTHONPATH=../.

Not_a_Golfer
quelle
11
Wann gehe ich cdin ein Verzeichnis? Ich renne py.testvon meiner Wurzel. es sei denn, ich irre mich und du meinst, wie Pytest durch meine Ordner geht
MattoTodd
Wenn es ein cdProblem wäre, würde ich es nicht auch auf dem Mac treffen?
MattoTodd
Oh, ich habe falsch verstanden und dachte, dass es im Testverzeichnis nicht funktioniert. Trotzdem würde der Trick in Vorschlag 1 funktionieren. Ich benutze nur Linux, daher kann ich das Verhalten unter anderen Betriebssystemen nicht erklären.
Not_a_Golfer
Haben Sie einen solchen Import für alle Ihre test.py-Dateien?
MattoTodd
4
Ja, aber meine Verzeichnisstruktur unterscheidet sich normalerweise geringfügig. Normalerweise behalte ich / src und / test im Stammverzeichnis.
Not_a_Golfer
275

Ich bin nicht sicher, warum py.test das aktuelle Verzeichnis nicht in PYTHONPATH selbst hinzufügt, aber hier ist eine Problemumgehung (die vom Stammverzeichnis Ihres Repositorys ausgeführt werden muss):

python -m pytest tests/

Es funktioniert, weil Python das aktuelle Verzeichnis im PYTHONPATH für Sie hinzufügt.

Apteryx
quelle
2
Relative Importe müssen in absolute umgeschrieben werden, wenn Sie den Code zum Ausführen der Anwendung nicht auf der Ebene haben, von der aus Sie den Befehl ausführen. Zum Beispiel: project/test/all-my-testsund project/src/app.pyund wegen dieser Änderung muss man das app.pyindirekt über eine __main__.pyDatei in aufrufen project/src, damit man den Aufruf verwenden kann python -m src. Soweit ich das beurteilen kann, ziemlich chaotisch.
Zelphir Kaltstahl
3
@Zelphir: Die Verwendung von absoluten Importen wird empfohlen. Habnabit's hat einen guten Artikel über Best Practices für das Verpacken: blog.habnab.it/blog/2013/07/21/python-packages-and-you , und PEP8 sagt, dass "implizite relative Importe niemals verwendet werden sollten und in Python entfernt wurden 3." Siehe: python.org/dev/peps/pep-0008 .
Apteryx
1
@Apteryx Du meinst "Projekt-absolut" oder? Weil Dinge wie /home/user/dev/projectxyz/src ...wirklich schlecht wären und in den meisten Fällen nicht auf anderen Maschinen laufen würden. Ich denke, was ich damit gemeint habe, ist, dass ich immer den gesamten Projektstamm in den Modulpfad schreiben muss, auch wenn sich ein Modul im selben Ordner befindet wie die Datei. Ich wusste nicht, dass dies als Best Practice angesehen wird, daher ist dies eine nützliche Information, danke. Ich stimme den meisten von pep8 zu, obwohl es immer noch nicht perfekt ist.
Zelphir Kaltstahl
1
@ Zelphir, ja, das habe ich gemeint. Ich glaube, der Begriff absolute Importe in Python bezieht sich immer auf "Projekt-absolut". Siehe: python.org/dev/peps/pep-0328/#rationale-for-absolute-imports . Tatsächlich bin ich mir ziemlich sicher, dass Sie nicht von zufälligen, absoluten Pfadpositionen importieren können, zumindest nicht mit dem Standardmechanismus "Importieren".
Apteryx
4
Ich habe __init__.pyin Tests hinzugefügt , dass das Problem gelöst ist. Jetzt kann ich verwendenpytest
Kiran Kumar Kotari
153

conftest Lösung

Die am wenigsten invasive Lösung ist das Hinzufügen einer leeren Datei mit dem Namen conftest.pyim repo/Verzeichnis:

$ touch repo/conftest.py

Das ist es. Sie müssen keinen benutzerdefinierten Code schreiben, um das zu entstellen, sys.pathoder Sie müssen daran denken, mitzuschleppen PYTHONPATHoder zu platzieren__init__.py mitzunehmen in Verzeichnisse zu in die er nicht gehört.

Das Projektverzeichnis danach:

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

Erläuterung

pytestsucht nach den conftestModulen in der Testsammlung, um benutzerdefinierte Hooks und Fixtures zu erfassen, und pytestfügtconftest.pysys.path zum Importieren der benutzerdefinierten Objekte das übergeordnete Verzeichnis von the zu (in diesem Fall dasrepo Verzeichnis) hinzu.

Andere Projektstrukturen

Wenn Sie eine andere Projektstruktur haben, platzieren Sie das conftest.pyim Paketstammverzeichnis (dasjenige, das Pakete enthält, aber selbst kein Paket ist, also kein enthält __init__.py), zum Beispiel:

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

src Layout

Obwohl dieser Ansatz mit dem srcLayout verwendet werden kann ( conftest.pyim srcVerzeichnis platzieren):

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

Vorsicht , dass das Hinzufügen srczu PYTHONPATHden Sinn und Nutzen des mildert srcLayout! Am Ende testen Sie den Code aus dem Repository und nicht das installierte Paket. Wenn Sie es tun müssen, brauchen Sie vielleicht das srcVerzeichnis überhaupt nicht.

Wohin von hier aus?

Natürlich sind conftestModule nicht nur einige Dateien, die bei der Erkennung des Quellcodes hilfreich sind. Hier finden alle projektspezifischen Verbesserungen des pytestFrameworks und die Anpassung Ihrer Testsuite statt. pytesthat viele Informationen zu conftestModulen, die in ihren Dokumenten verteilt sind ; Beginnen Sie mit conftest.py: lokalen Plugins pro Verzeichnis

Außerdem hat SO eine ausgezeichnete Frage zu conftestModulen: Wozu werden in py.test conftest.py-Dateien verwendet?

hoefling
quelle
2
@ aaa90210 Obwohl ich Ihr Problem nicht reproduzieren kann (das Importieren von einem Conftest in ein Root-Verzeichnis funktioniert auf jeder Ebene), sollten Sie niemals aus Conftest-Dateien importieren, da dies ein reservierter Name ist pytestund dringend davon abgeraten wird. Auf diese Weise pflanzen Sie Samen für zukünftige Fehler. Erstellen Sie ein anderes Modul mit dem Namen utils.pyund platzieren Sie dort den Code für die Wiederverwendung in Tests.
Hacke
4
Daumen hoch! Dies ist die einzige Lösung, die für mich gut funktioniert.
130.
4
sollte unbedingt die akzeptierte Antwort sein - danke!
Martin Peck
2
Logischerweise gehört conftest.pyes nicht zum Anwendungscode und imo ist es src/nicht korrekt , es unter zu platzieren .
Nik O'Lai
2
Diese Antwort sollte ein fester Header für SO sein. Vielen Dank.
Rigoberta Raviolini
119

Ich hatte das gleiche Problem. Ich habe es behoben, indem ich __init__.pymeinem testsVerzeichnis eine leere Datei hinzugefügt habe .

Aron Curzon
quelle
77
Beachten Sie, dass dies von py.test NICHT empfohlen wird: 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
Ich kam mit der gleichen Frage hierher und fand, dass das Entfernen __init__.pyaus meinem Testverzeichnis es für mich gelöst hat.
101
3
@mafro Ich sehe das Problem nicht? Die Tests müssen kein importierbarer Code sein, sie werden von Ihrem Testläufer gefunden. Nur der zu testende Code sollte ein installiertes Paket / Modul sein, nicht die Tests.
K.-Michael Aye
5
Durch Hinzufügen eines __init__.pyIn-Unterverzeichnisses von test/funktioniert der absolute Import für die Ausführung bestimmter Tests in diesem Unterverzeichnis für zu installierende Module. Vielen Dank.
Bryce Guinta
7
Los geht's: doc.pytest.org/en/latest/goodpractices.html Mit Google wirklich leicht zu finden.
K.-Michael Aye
46

Führen Sie pytestsich als Modul aus mit: python -m pytest tests

Stefano Messina
quelle
3
Dies scheint eine funktionierende Lösung zu sein, aber kann jemand erklären, WARUM? Ich würde lieber die zugrunde liegende Ursache beheben, als sie nur python -m pytestohne eine andere Erklärung als "weil es funktioniert" zu verwenden
Janne Enberg
4
Dies geschieht beispielsweise, wenn die Projekthierarchie: package/src package/testsund in testsSie importiert wird src. Bei der Ausführung als Modul werden Importe als absolut und nicht relativ zum Ausführungsort betrachtet.
Stefano Messina
1
Diese Lösung hat mir geholfen, danke! Die Ursache dafür war der Konflikt in der Python-Version. Der Pytest-Test funktioniert für frühere Python-Versionen. In meiner Situation ist meine Python-Version 3.7.1, Python-m-Pytest-Tests funktionieren, aber keine Pytest-Tests.
Ruxi Zhang
1
Von Pytest "Das Ausführen von pytest mit python -m pytest [...] anstelle von pytest [...] führt zu einem nahezu gleichwertigen Verhalten, außer dass beim vorherigen Aufruf das aktuelle Verzeichnis zu sys.path hinzugefügt wird."
Moad Ennagi
37

Sie können mit PYTHONPATH im Projektstamm ausführen

PYTHONPATH=. py.test

Oder verwenden Sie pip install als bearbeitbaren Import

pip install -e .   # install package using setup.py in editable mode
Ford Guo
quelle
3
Das funktionierte bei mir nicht mit einem testVerzeichnis, das nicht in der srcVerzeichnisstruktur vorliegt und aus dem Verzeichnis aufruft, das sowohl testals auch das Verzeichnis enthält src.
Zelphir Kaltstahl
21

Ich habe dies als Antwort auf Ihre Frage und meine eigene Verwirrung erstellt. Ich hoffe, es hilft. Achten Sie sowohl in der py.test-Befehlszeile als auch in der tox.ini auf PYTHONPATH.

https://github.com/jeffmacdonald/pytest_test

Insbesondere: Sie müssen py.test und tox mitteilen, wo sich die Module befinden, die Sie einschließen.

Mit py.test können Sie dies tun:

PYTHONPATH=. py.test

Und mit tox fügen Sie dies Ihrer tox.ini hinzu:

[testenv]
deps= -r{toxinidir}/requirements.txt
commands=py.test
setenv =
    PYTHONPATH = {toxinidir}
Jeff MacDonald
quelle
1
Können Sie eine kurze Erklärung für das von Ihnen verknüpfte Projekt geben?
JF Meier
1
Vielleicht bin ich es nur, aber die README-Datei zum Projekt ist ziemlich detailliert, und mein Kommentar zum Stackoverflow sagt, warum ich das Repo erstellt habe.
Jeff MacDonald
5
Obwohl dies nicht unbedingt erforderlich ist, ist es üblich, den Hauptinhalt einer Antwort in der Antwort selbst zu haben, da dadurch sichergestellt wird, dass die Antwort in x Jahren verständlich ist, wenn die verknüpfte Ressource möglicherweise schon lange nicht mehr vorhanden ist.
JF Meier
:) Naja. Das ist das Internet für dich.
Jeff MacDonald
9

Ich hatte das gleiche Problem in Flask.

Als ich hinzufügte:

__init__.py

zum Testordner, Problem verschwunden :)

Wahrscheinlich konnte die Anwendung Ordnertests nicht als Modul erkennen

user13037517
quelle
8

Ich habe es behoben, indem ich die oberste Ebene __init__.pyim übergeordneten Ordner meiner Quellen entfernt habe.

Gonzalo
quelle
1
Es wurde für mich behoben. Kann das jemand erklären?
Aboger
das hier hat es auch für mich behoben.
Ich
Ich habe init .py hinzugefügt, hatte aber immer noch die gleichen Probleme, aber diese Lösung hat auch bei mir funktioniert. Grund bitte?
Abhijit
7

Ich bekam seltsame ConftestImportFailure: ImportError('No module named ...Fehler, als ich versehentlich eine __init__.pyDatei zu meinem src-Verzeichnis hinzugefügt hatte (das kein Python-Paket sein sollte, sondern nur ein Container aller Quellen).

jbasko
quelle
3

Ich habe diesen Fehler aufgrund von etwas noch Einfacherem erhalten (man könnte sogar Triviales sagen). Ich hatte das pytestModul nicht installiert . Also einfachapt install python-pytest Problem für mich behoben.

'pytest' wäre in setup.py als Testabhängigkeit aufgeführt worden. Stellen Sie sicher, dass Sie auch die Testanforderungen installieren.

craq
quelle
3

Ich hatte ein ähnliches Problem. pytestIch habe kein Modul erkannt, das in der Umgebung installiert ist, in der ich gearbeitet habe.

Ich habe es behoben, indem ich es auch pytestin derselben Umgebung installiert habe .

Nocibambi
quelle
Obwohl ich pytest aus einem venv heraus verwendet habe, hatte ich es auch global installiert, was mir diesen Fehler gab. Nach der Deinstallation der globalen Version und der Installation im venv hat es funktioniert.
Markus Ressel
2

Für mich wurde das Problem tests.pyvon Django zusammen mit dem testsVerzeichnis generiert . Das Entfernen tests.pylöste das Problem.

Paweł Mucha
quelle
2

Ich habe diesen Fehler erhalten, weil ich relative Importe falsch verwendet habe. Im OP-Beispiel sollte test_app.py Funktionen importieren, indem z

from repo.app import *

Obwohl __init__.py-Dateien großzügig über die Dateistruktur verteilt sind, funktioniert dies nicht und erzeugt die Art von ImportError, die angezeigt wird, es sei denn, die Dateien und Testdateien befinden sich im selben Verzeichnis.

from app import *

Hier ist ein Beispiel dafür, was ich mit einem meiner Projekte zu tun hatte:

Hier ist meine Projektstruktur:

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

Um über test_activity_indicator.py auf activity_indicator.py zugreifen zu können, musste ich:

  • Starten Sie test_activity_indicatory.py mit dem richtigen relativen Import:
    from microbit.activity_indicator.activity_indicator import *
  • Fügen Sie __init__.py-Dateien in die gesamte Projektstruktur ein:
    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
quelle
0

Sehr oft wurden die Tests unterbrochen, weil das Modul nicht importiert werden konnte. Nach Recherchen stellte ich fest, dass das System die Datei an der falschen Stelle betrachtet und wir das Problem leicht überwinden können, indem wir die Datei mit dem Modul in kopieren der gleiche Ordner wie angegeben, um ordnungsgemäß importiert zu werden. Ein weiterer Lösungsvorschlag wäre, die Deklaration für den Import zu ändern und MutPy den korrekten Pfad der Einheit anzuzeigen. Aufgrund der Tatsache, dass mehrere Einheiten diese Abhängigkeit haben können, was bedeutet, dass wir Änderungen auch in ihren Deklarationen festschreiben müssen, ziehen wir es vor, die Einheit einfach in den Ordner zu verschieben.

Baydaa
quelle
Es gibt andere Antworten, die die Frage des OP stellen, und sie wurden vor einiger Zeit veröffentlicht. Stellen Sie beim Posten einer Antwort sicher, dass Sie entweder eine neue Lösung oder eine wesentlich bessere Erklärung hinzufügen, insbesondere wenn Sie ältere Fragen beantworten. Manchmal ist es besser, einen Kommentar zu einer bestimmten Antwort zu schreiben.
help-info.de
Zusätzlich zum Kommentar von @ help-info.de: Hier ist der Link zum Leitfaden zur Beantwortung von Fragen: stackoverflow.com/help/how-to-answer
die Hand von NOD
0

Laut einem Beitrag auf Medium von Dirk Avery (und unterstützt durch meine persönliche Erfahrung) können Sie keine systemweite Installation von pytest verwenden, wenn Sie eine virtuelle Umgebung für Ihr Projekt verwenden. Sie müssen es in der virtuellen Umgebung installieren und diese Installation verwenden.

Insbesondere wenn Sie es an beiden Stellen installiert haben, pytestfunktioniert das einfache Ausführen des Befehls nicht, da die Systeminstallation verwendet wird. Wie die anderen Antworten beschrieben haben, eine einfache Lösung laufen python -m pyteststatt pytest; Dies funktioniert, weil die Umgebungsversion von pytest verwendet wird. Alternativ können Sie auch die Systemversion von pytest deinstallieren. Nach der Reaktivierung der virtuellen Umgebung sollte der pytestBefehl funktionieren.

Einhaender
quelle
Das einzige, was bisher für mich funktioniert hat, war python -m pytest tests/.
Nik O'Lai
0

Ich hatte das gleiche Problem, als ich dem Flask-Tutorial folgte, und fand die Antwort in den offiziellen Pytest- Dokumenten. Es ist eine kleine Abweichung von der Art und Weise, wie ich (und ich denke, viele andere) es gewohnt bin, Dinge zu tun.

Sie müssen eine setup.pyDatei im Stammverzeichnis Ihres Projekts mit mindestens den folgenden zwei Zeilen erstellen :

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

Dabei ist PACKAGENAME der Name Ihrer App. Dann müssen Sie es mit pip installieren:

pip install -e .

Das -eFlag weist pip an, das Paket im bearbeitbaren oder "Entwicklungs" -Modus zu installieren. Wenn Sie pytestes das nächste Mal ausführen , sollte Ihre App im Standard enthalten sein PYTHONPATH.

Luis Lezcano Airaldi
quelle
0

Meine Lösung:

Erstellen Sie die conftest.pyDatei in dem testVerzeichnis, das Folgendes enthält:

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

Dadurch wird der gewünschte Ordner zum Python-Pfad hinzugefügt, ohne dass jede Testdatei geändert , die env-Variable festgelegt oder absolute / relative Pfade verwendet werden müssen.

David Burba
quelle