Je viens d'être configuré pour utiliser pytest avec Python 2.6. Cela a bien fonctionné jusqu'à présent à l'exception de la gestion des instructions "import": je n'arrive pas à obtenir pytest pour répondre aux importations dans de la même manière que mon programme.
Ma structure de répertoire est la suivante:
src/
main.py
util.py
test/
test_util.py
geom/
vector.py
region.py
test/
test_vector.py
test_region.py
Pour courir, j'appelle python main.py
de src /.
Dans main.py, j'importe le vecteur et la région avec
from geom.region import Region
from geom.vector import Vector
Dans vector.py, j'importe la région avec
from geom.region import Region
Tout cela fonctionne bien lorsque j'exécute le code lors d'une exécution standard. Cependant, lorsque j'appelle "py.test" depuis src /, il se termine systématiquement avec des erreurs d'importation.
Mon premier problème était que, lors de l'exécution de "test/test_foo.py", py.test ne pouvait pas "importer foo.py" directement. J'ai résolu cela en utilisant l'outil "imp". Dans "test_util.py":
import imp
util = imp.load_source("util", "util.py")
Cela fonctionne très bien pour de nombreux fichiers. Cela semble également impliquer que lorsque pytest exécute "path/test/test_foo.py" pour tester "path/foo.py", il est basé dans le répertoire "path".
Cependant, cela échoue pour "test_vector.py". Pytest peut trouver et importer le module vector
, mais il ne peut pas localiser les importations de vector
. Les importations suivantes (depuis "vector.py") échouent toutes les deux lors de l'utilisation de pytest:
from geom.region import *
from region import *
Ces deux donnent des erreurs de forme
ImportError: No module named [geom.region / region]
Je ne sais pas quoi faire ensuite pour résoudre ce problème; ma compréhension des importations en Python est limitée.
Quelle est la bonne façon de gérer les importations lors de l'utilisation de pytest?
Dans vector.py
, J'ai modifié l'instruction d'importation de
from geom.region import Region
simplement
from region import Region
Cela rend l'importation relative au répertoire "vector.py".
Ensuite, dans "test/test_vector.py", j'ajoute le répertoire "vector.py" au chemin comme suit:
import sys, os
sys.path.append(os.path.realpath(os.path.dirname(__file__)+"/.."))
Cela permet à Python de trouver "../region.py" dans "geom/test/test_vector.py".
Cela fonctionne, mais cela semble extrêmement problématique car j'ajoute une tonne de nouveaux répertoires au chemin. Ce que je recherche c'est soit
1) Une stratégie d'importation compatible avec pytest, ou
2) Une option dans pytest qui le rend compatible avec ma stratégie d'importation
Je laisse donc cette question ouverte pour des réponses de ce type.
import recherche dans les répertoires suivants un module:
sys.path est le résultat de la combinaison du répertoire personnel, PYTHONPATH et le répertoire bibliothèques standard. Ce que vous faites, modifier sys.path est correct. C'est quelque chose que je fais régulièrement. Vous pouvez essayer d'utiliser PYTHONPATH si vous n'aimez pas jouer avec sys.path
Le problème ici est que Pytest parcourt le système de fichiers pour découvrir les fichiers qui contiennent des tests, mais doit ensuite générer un nom de module qui provoquera le chargement de ce fichier par import
. (N'oubliez pas, les fichiers ne sont pas des modules .)
Pytest propose ceci nom du package de test en recherchant le premier répertoire au niveau ou au-dessus du niveau du fichier qui ne comprend pas de fichier __init__.py
Et en déclarant que le "basedir" du module arborescence contenant un module généré à partir de ce fichier. Il ajoute ensuite le basedir à sys.path
Et importe en utilisant le nom du module qui trouvera ce fichier par rapport au basedir.
Il y a quelques implications de cela dont vous devez vous méfier:
Le chemin de base peut ne pas correspondre à votre chemin de base prévu, auquel cas le module aura un nom qui ne correspond pas à ce que vous utiliseriez normalement. Par exemple, ce que vous pensez être geom.test.test_vector
Sera en fait nommé juste test_vector
Pendant l'exécution de Pytest car il n'a trouvé aucun __init__.py
Dans src/geom/test/
Et a donc ajouté ce répertoire à sys.path
.
Vous pouvez rencontrer des collisions de dénomination de module si deux fichiers dans des répertoires différents ont le même nom. Par exemple, en l'absence de fichiers __init__.py
N'importe où, l'ajout de geom/test/test_util.py
Entrera en conflit avec test/test_util.py
Car les deux sont chargés en tant que import test_util.py
, Avec à la fois test/
Et geom/test/
Dans le chemin.
Le système que vous utilisez ici, sans modules explicites __init__.py
, A Python create packages d'espace de noms implicites pour vos répertoires. (Un package est un avec des sous-modules.) Idéalement, nous configurerions Pytest avec un chemin à partir duquel il générerait également cela, mais il ne semble pas savoir comment le faire.
La solution la plus simple ici consiste simplement à ajouter des fichiers __init__.py
Vides à tous les sous-répertoires sous src/
; cela entraînera Pytest à tout importer en utilisant des noms de package/module qui commencent par des noms de répertoire sous src/
.
La question Comment puis-je tester un projet en utilisant des packages d'espace de noms PEP 420? discute d'autres solutions à cela.
Je me demandais aussi quoi faire à propos de ce problème. Après avoir lu ce post et joué un peu, j'ai trouvé une solution élégante. J'ai créé un fichier appelé "test_setup.py" et y ai mis le code suivant:
import sys, os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
J'ai mis ce fichier dans le répertoire de niveau supérieur (tel que src). Lorsque pytest
est exécuté à partir du répertoire de niveau supérieur, il exécutera tous les fichiers de test, y compris celui-ci puisque le fichier est préfixé par "test". Il n'y a aucun test dans le fichier, mais il est toujours exécuté car il commence par "test".
Le code ajoutera le nom de répertoire actuel du fichier test_setup.py au chemin d'accès système dans l'environnement de test. Cela ne sera fait qu'une seule fois, il n'y a donc pas beaucoup de choses ajoutées au chemin.
Ensuite, à partir de n'importe quelle fonction de test, vous pouvez importer des modules relatifs à ce dossier de niveau supérieur (tels que import geom.region
) et il sait où le trouver puisque le répertoire src a été ajouté au chemin.
Si vous souhaitez exécuter un seul fichier de test (tel que test_util.py) au lieu de tous les fichiers, vous utiliseriez:
pytest test_setup.py test\test_util.py
Cela exécute à la fois le code test_setup et test_util afin que le code test_setup puisse toujours être utilisé.