python - tutorial - pytest test class
Importando correctamente con pytest (3)
El problema aquí es que Pytest recorre el sistema de archivos para descubrir archivos que contienen pruebas, pero luego necesita generar un nombre de módulo que haga que la import
cargue ese archivo. (Recuerda, los archivos no son módulos ).
Pytest presenta este nombre de paquete de prueba al encontrar el primer directorio en el nivel del archivo o por encima del mismo que no incluye un archivo __init__.py
y declarar que "basedir" para el árbol de módulos que contiene un módulo generado a partir de este archivo. Luego agrega el basedir al frente de sys.path
e importa usando el nombre del módulo que encontrará ese archivo en relación con el basedir.
Hay algunas implicaciones de esto de las que debes tener cuidado:
Es posible que la ruta base no coincida con la base prevista, en cuyo caso el módulo tendrá un nombre que no coincide con el que usaría normalmente. Por ejemplo, lo que creas que es
geom.test.test_vector
se llamará simplementetest_vector
durante la ejecución de Pytest porque no encontró__init__.py
ensrc/geom/test/
y así lo agregó a la ruta.Puede encontrarse con colisiones de nomenclatura de módulos si dos archivos en directorios diferentes tienen el mismo nombre. Por ejemplo, al carecer de los archivos
__init__.py
cualquier lugar, agregargeom/test/test_util.py
entrará en conflicto contest/test_util.py
porque ambos se cargan comoimport test_util.py
, contest/
ygeom/test/
en la ruta.
El sistema que está utilizando aquí, sin módulos __init__.py
explícitos, hace que Python cree paquetes de espacio de nombres implícitos para sus directorios. (Un paquete es un módulo con submódulos.) Lo ideal sería configurar Pytest con una ruta desde la cual también generaría esto, pero no parece saber cómo hacerlo.
La solución más sencilla aquí es simplemente agregar archivos __init__.py
vacíos a todos los subdirectorios bajo src/
; esto hará que Pytest importe todo usando los nombres de paquete / módulo que comienzan con los nombres de directorio bajo src/
.
Acabo de configurarme para usar pytest con Python 2.6. Hasta ahora ha funcionado bien, con la excepción del manejo de las declaraciones de "importación": parece que no puedo ponerme en evidencia para responder a las importaciones de la misma manera que mi programa.
Mi estructura de directorios es la siguiente:
src/
main.py
util.py
test/
test_util.py
geom/
vector.py
region.py
test/
test_vector.py
test_region.py
Para ejecutar, llamo a python main.py
desde src /.
En main.py, importo tanto vector como región con
from geom.region import Region
from geom.vector import Vector
En vector.py, importo región con
from geom.region import Region
Todo esto funciona bien cuando ejecuto el código en una ejecución estándar. Sin embargo, cuando llamo "py.test" desde src /, sale constantemente con errores de importación.
Algunos problemas y mis intentos de solución
Mi primer problema fue que, al ejecutar "test / test_foo.py", py.test no podía "importar foo.py" directamente. Resolví esto usando la herramienta "imp". En "test_util.py":
import imp
util = imp.load_source("util", "util.py")
Esto funciona muy bien para muchos archivos. También parece implicar que cuando pytest ejecuta "path / test / test_foo.py" para probar "path / foo.py", se basa en el directorio "path".
Sin embargo, esto falla para "test_vector.py". Pytest puede encontrar e importar el módulo de vector
, pero no puede localizar ninguna de las importaciones de vector
. Las siguientes importaciones (de "vector.py") fallan cuando se usa pytest:
from geom.region import *
from region import *
Ambos dan errores de la forma.
ImportError: No module named [geom.region / region]
No sé qué hacer a continuación para resolver este problema; mi comprensión de las importaciones en Python es limitada.
¿Cuál es la forma correcta de manejar las importaciones cuando se usa pytest?
Edit: Extremadamente Hacky Solution
En vector.py
, cambié la declaración de importación de
from geom.region import Region
simplemente
from region import Region
Esto hace la importación relativa al directorio de "vector.py".
A continuación, en "test / test_vector.py", agrego el directorio de "vector.py" a la ruta de la siguiente manera:
import sys, os
sys.path.append(os.path.realpath(os.path.dirname(__file__)+"/.."))
Esto permite a Python encontrar "../region.py" en "geom / test / test_vector.py".
Esto funciona, pero parece extremadamente problemático porque estoy agregando un montón de nuevos directorios a la ruta. Lo que busco es cualquiera
1) Una estrategia de importación que sea compatible con pytest, o
2) Una opción en pytest que la hace compatible con mi estrategia de importación
Así que estoy dejando esta pregunta abierta para respuestas de este tipo.
Me preguntaba qué hacer con este problema también. Después de leer este post y de jugar un poco, descubrí una solución elegante. Creé un archivo llamado "test_setup.py" y puse el siguiente código en él:
import sys, os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
Puse este archivo en el directorio de nivel superior (como src). Cuando se ejecuta pytest
desde el directorio de nivel superior, ejecutará todos los archivos de prueba, incluido este, ya que el archivo tiene el prefijo "prueba". No hay pruebas en el archivo, pero aún se ejecuta ya que comienza con "prueba".
El código agregará el nombre de directorio actual del archivo test_setup.py a la ruta del sistema dentro del entorno de prueba. Esto se hará solo una vez, por lo que no hay un montón de cosas agregadas al camino.
Luego, desde cualquier función de prueba, puede importar módulos relativos a esa carpeta de nivel superior (como import geom.region
) y sabe dónde encontrarlo desde que se agregó el directorio src a la ruta.
Si desea ejecutar un solo archivo de prueba (como test_util.py) en lugar de todos los archivos, usaría:
pytest test_setup.py test/test_util.py
Esto ejecuta el código test_setup y test_util para que el código test_setup aún pueda ser utilizado.
Importa las búsquedas en los siguientes directorios para encontrar un módulo:
- El directorio de inicio del programa. Este es el directorio de su script de root. Cuando está ejecutando pytest, su directorio de inicio es donde está instalado (/ usr / local / bin probablemente). No importa si lo está ejecutando desde su directorio src porque la ubicación de su pytest determina su directorio de inicio. Esa es la razón por la que no encuentra los módulos.
- PYTHONPATH . Esta es una variable de entorno. Puede configurarlo desde la línea de comando de su sistema operativo. En sistemas Linux / Unix, puede hacer esto ejecutando: '' exportar PYTHONPATH = / su / custom / ruta '' Si desea que Python encuentre sus módulos desde el directorio de prueba, debe incluir la ruta src en esta variable.
- El directorio de las bibliotecas estándar . Este es el directorio donde están instaladas todas tus bibliotecas.
- Hay una opción menos común usando un archivo pth .
sys.path es el resultado de combinar el directorio de inicio , PYTHONPATH y el directorio de bibliotecas estándar . Lo que estás haciendo, modificando sys.path es correcto. Es algo que hago regularmente. Puedes intentar usar PYTHONPATH si no te gusta jugar con sys.path