pytest Importmechanismen und sys.path/PYTHONPATH¶
Importmodi¶
pytest benötigt als Testframework Importmechanismen für Testmodule und conftest.py Dateien zur Ausführung.
Das Importieren von Dateien in Python ist ein nicht trivialer Prozess, daher können Aspekte des Importprozesses über das Kommandozeilenflag --import-mode gesteuert werden, das folgende Werte annehmen kann:
prepend(Standard): Der Verzeichnispfad, der jedes Modul enthält, wird am Anfang vonsys.patheingefügt, falls es dort noch nicht vorhanden ist, und dann mit derimportlib.import_moduleFunktion importiert.Es wird dringend empfohlen, Ihre Testmodule als Pakete zu organisieren, indem Sie
__init__.pyDateien in Ihren Testverzeichnissen hinzufügen. Dadurch werden die Tests Teil eines ordnungsgemäßen Python-Pakets, was es pytest ermöglicht, ihren vollständigen Namen aufzulösen (z. B.tests.core.test_corefürtest_core.pyinnerhalb destests.corePakets).Wenn der Testverzeichnisbaum nicht als Pakete organisiert ist, muss jede Testdatei einen eindeutigen Namen im Vergleich zu anderen Testdateien haben, andernfalls löst pytest einen Fehler aus, wenn es zwei Tests mit demselben Namen findet.
Dies ist der klassische Mechanismus, der aus der Zeit stammt, als Python 2 noch unterstützt wurde.
append: Das Verzeichnis, das jedes Modul enthält, wird am Ende vonsys.pathangehängt, falls es dort noch nicht vorhanden ist, und mitimportlib.import_moduleimportiert.Dies ermöglicht es Benutzern besser, Testmodule gegen installierte Versionen eines Pakets auszuführen, auch wenn das zu testende Paket denselben Import-Root hat. Zum Beispiel
testing/__init__.py testing/test_pkg_under_test.py pkg_under_test/
die Tests laufen gegen die installierte Version von
pkg_under_test, wenn--import-mode=appendverwendet wird, während sie mitprependdie lokale Version aufnehmen würden. Diese Art von Verwirrung ist der Grund, warum wir die Verwendung von Src-Layouts befürworten.Gleich wie
prepend, erfordert es eindeutige Testmodulnamen, wenn der Testverzeichnisbaum nicht in Paketen organisiert ist, da die Module nach dem Import insys.modulesplatziert werden.
importlib: Dieser Modus verwendet feinere Kontrollmechanismen, die vonimportlibbereitgestellt werden, um Testmodule zu importieren, ohnesys.pathzu ändern.Vorteile dieses Modus
pytest wird
sys.pathüberhaupt nicht ändern.Testmodulnamen müssen nicht eindeutig sein – pytest generiert automatisch einen eindeutigen Namen basierend auf dem
rootdir.
Nachteile
Testmodule können sich nicht gegenseitig importieren.
Testdienstprogramme in den Testverzeichnissen (z. B. ein
tests.helpersModul mit testbezogenen Funktionen/Klassen) sind nicht importierbar. Die Empfehlung in diesem Fall ist, testbezogene Dienstprogramme zusammen mit dem Anwendungs-/Bibliothekscode zu platzieren, z. B.app.testing.helpers.Wichtig: Mit "Testdienstprogramme" meinen wir Funktionen/Klassen, die direkt von anderen Tests importiert werden; dies schließt keine Fixtures ein, die in
conftest.pyDateien platziert werden sollten, zusammen mit den Testmodulen, und die automatisch von pytest erkannt werden.
So funktioniert es
Gegeben einen bestimmten Modulpfad, z. B.
tests/core/test_models.py, wird ein kanonischer Name wietests.core.test_modelsabgeleitet und versucht, ihn zu importieren.Für Nicht-Testmodule funktioniert dies, wenn sie über
sys.patherreichbar sind. Zum Beispiel wird.env/lib/site-packages/app/core.pyalsapp.coreimportierbar sein. Dies geschieht, wenn Plugins Nicht-Testmodule importieren (z. B. Doctesting).Wenn dieser Schritt erfolgreich ist, wird das Modul zurückgegeben.
Für Testmodule, es sei denn, sie sind von
sys.patherreichbar, wird dieser Schritt fehlschlagen.Wenn der vorherige Schritt fehlschlägt, importieren wir das Modul direkt mit
importlibEinrichtungen, was es uns ermöglicht, es zu importieren, ohnesys.pathzu ändern.Da Python das Modul auch in
sys.modulesverfügbar haben muss, leitet pytest einen eindeutigen Namen daraus ab, basierend auf seiner relativen Position zumrootdir, und fügt das Modul zusys.moduleshinzu.Zum Beispiel wird
tests/core/test_models.pyals das Modultests.core.test_modelsimportiert.
Hinzugefügt in Version 6.0.
Hinweis
Ursprünglich beabsichtigten wir, importlib in zukünftigen Versionen zum Standard zu machen, aber es ist jetzt klar, dass es seine eigenen Nachteile hat, sodass der Standard für absehbare Zeit prepend bleiben wird.
Hinweis
Standardmäßig versucht pytest nicht, Namensraum-Pakete automatisch aufzulösen, aber das kann über die Konfigurationsvariable consider_namespace_packages geändert werden.
Siehe auch
Die Konfigurationsvariable pythonpath.
Die Konfigurationsvariable consider_namespace_packages.
Importmodus-Szenarien prepend und append¶
Hier ist eine Liste von Szenarien, bei denen die Importmodi prepend oder append verwendet werden und pytest sys.path ändern muss, um Testmodule oder conftest.py Dateien zu importieren, sowie die Probleme, auf die Benutzer dadurch stoßen könnten.
Testmodule / conftest.py Dateien innerhalb von Paketen¶
Betrachten Sie diese Datei- und Verzeichnisstruktur
root/
|- foo/
|- __init__.py
|- conftest.py
|- bar/
|- __init__.py
|- tests/
|- __init__.py
|- test_foo.py
Bei Ausführung von
pytest root/
findet pytest foo/bar/tests/test_foo.py und erkennt, dass es Teil eines Pakets ist, da sich eine __init__.py Datei im selben Ordner befindet. Es sucht dann aufwärts, bis es den letzten Ordner findet, der immer noch eine __init__.py Datei enthält, um den Paket-Root (in diesem Fall foo/) zu finden. Um das Modul zu laden, wird root/ an den Anfang von sys.path (falls noch nicht vorhanden) eingefügt, um test_foo.py als das Modul foo.bar.tests.test_foo zu laden.
Die gleiche Logik gilt für die conftest.py Datei: sie wird als foo.conftest Modul importiert.
Die Beibehaltung des vollständigen Paketnamens ist wichtig, wenn Tests in einem Paket leben, um Probleme zu vermeiden und Testmodulen doppelte Namen zu ermöglichen. Dies wird auch ausführlich in Konventionen für die Python-Testentdeckung diskutiert.
Eigenständige Testmodule / conftest.py Dateien¶
Betrachten Sie diese Datei- und Verzeichnisstruktur
root/
|- foo/
|- conftest.py
|- bar/
|- tests/
|- test_foo.py
Bei Ausführung von
pytest root/
pytest findet foo/bar/tests/test_foo.py und erkennt, dass es NICHT Teil eines Pakets ist, da sich keine __init__.py Datei im selben Ordner befindet. Es fügt dann root/foo/bar/tests zu sys.path hinzu, um test_foo.py als das Modul test_foo zu importieren. Dasselbe geschieht mit der conftest.py Datei, indem root/foo zu sys.path hinzugefügt wird, um sie als conftest zu importieren.
Aus diesem Grund kann dieses Layout keine Testmodule mit demselben Namen haben, da sie alle im globalen Import-Namespace importiert werden.
Dies wird auch ausführlich in Konventionen für die Python-Testentdeckung diskutiert.
Aufruf von pytest versus python -m pytest¶
Das Ausführen von pytest mit pytest [...] anstelle von python -m pytest [...] ergibt ein fast äquivalentes Verhalten, mit der Ausnahme, dass letzteres das aktuelle Verzeichnis zu sys.path hinzufügt, was dem Standardverhalten von python entspricht.
Siehe auch Aufruf von pytest über python -m pytest.