web-dev-qa-db-fra.com

Importer correctement avec pytest

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.


Certains problèmes et mes tentatives de solution

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?


Edit: Solution extrêmement Hacky

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.

31
NcAdams

import recherche dans les répertoires suivants un module:

  1. Le répertoire personnel du programme. Il s'agit du répertoire de votre script racine. Lorsque vous exécutez pytest, votre répertoire personnel est l'endroit où il est installé (/ usr/local/bin probablement). Peu importe que vous l'exécutiez à partir de votre répertoire src car l'emplacement de votre pytest détermine votre répertoire personnel. C'est la raison pour laquelle il ne trouve pas les modules.
  2. CHEMIN PYTHON . Il s'agit d'une variable d'environnement. Vous pouvez le définir à partir de la ligne de commande de votre système d'exploitation. Dans les systèmes Linux/Unix, vous pouvez le faire en exécutant: ' export PYTHONPATH =/your/custom/path ' Si vous vouliez Python pour trouver vos modules dans le répertoire de test, vous devez inclure le chemin src dans cette variable.
  3. Le répertoire bibliothèques standard. Il s'agit du répertoire dans lequel toutes vos bibliothèques sont installées.
  4. Il existe une option moins courante utilisant un fichier pth.

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

14
Jose Varez

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:

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

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

11
Curt J. Sampson

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

6
Joe Dimig