Problema de RUTA con pytest 'ImportError: ningún módulo llamado YadaYadaYada'

231

Utilicé easy_install para instalar pytest en una Mac y comencé a escribir pruebas para un proyecto con una estructura de archivos como esta:

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

ejecutar py.testmientras está en el directorio repos, todo se comporta como cabría esperar

pero cuando intento lo mismo en Linux o Windows (ambos tienen Pytest 2.2.3), ladra cada vez que llega a la primera importación de algo desde la ruta de mi aplicación. Decir por ejemplofrom app import some_def_in_app

¿Necesito editar mi RUTA para ejecutar py.test en estos sistemas? ¿Alguien ha experimentado esto?

MattoTodd
fuente
44
Aquí está la forma de solucionarlo con setuptools.
Ederag
44
Verifique la respuesta @hoefling y considere cambiar su respuesta aceptada, si SO lo permite después de tanto tiempo: ¡mucho mejor!
Davide

Respuestas:

91

Sí, la carpeta de origen no está en la ruta de Python si va cdal directorio de pruebas.

Tienes 2 opciones:

  1. Agregue la ruta manualmente a los archivos de prueba, algo como esto:

    import sys, os
    myPath = os.path.dirname(os.path.abspath(__file__))
    sys.path.insert(0, myPath + '/../')
  2. Ejecute las pruebas con la env var PYTHONPATH=../.

Not_a_Golfer
fuente
11
cuando estoy cdyendo a un directorio? Estoy corriendo py.testdesde mi raíz. si no me equivoco y que decir como PYtest camina a través de mis carpetas
MattoTodd
si fuera un cdproblema, ¿no lo golpearía también en Mac?
MattoTodd
Oh, leí mal y pensé que no funciona desde el directorio de pruebas. todavía el truco en la sugerencia 1 funcionaría. Solo uso Linux, así que no puedo explicar el comportamiento en otros sistemas operativos.
Not_a_Golfer
¿tiene una importación como esa en todos sus archivos test.py?
MattoTodd
44
sí, pero la estructura de mi directorio suele ser ligeramente diferente: generalmente mantengo / src y / test en el directorio raíz.
Not_a_Golfer
276

No estoy seguro de por qué py.test no agrega el directorio actual en PYTHONPATH, pero aquí hay una solución alternativa (que se ejecutará desde la raíz de su repositorio):

python -m pytest tests/

Funciona porque Python agrega el directorio actual en PYTHONPATH por usted.

Ápterix
fuente
2
Requiere reescribir las importaciones relativas a las absolutas, si tiene el código para ejecutar la aplicación no en el nivel, desde donde ejecuta el comando. Por ejemplo: project/test/all-my-testsy project/src/app.pydebido a ese cambio, uno necesita llamar app.pyindirectamente usando un __main__.pyarchivo project/src, para poder usar la llamada python -m src. Bastante desordenado por lo que puedo decir.
Zelphir Kaltstahl
3
@Zelphir: Usar importaciones absolutas es una práctica recomendada. Habnabit tiene un buen artículo sobre las mejores prácticas de empaquetado: blog.habnab.it/blog/2013/07/21/python-packages-and-you , y PEP8 dice que "las importaciones relativas implícitas nunca deberían usarse y se han eliminado en Python 3. " Ver: python.org/dev/peps/pep-0008 .
Apteryx el
1
@Apteryx ¿Quieres decir "proyecto absoluto", verdad? Porque cosas como /home/user/dev/projectxyz/src ...serían realmente malas y no se ejecutarían en otras máquinas en la mayoría de los casos. Creo que lo que quise decir es que siempre tengo que escribir toda la raíz del proyecto a la ruta del módulo, incluso si un módulo está en la misma carpeta que el archivo ejecutado. No sabía que esto se considera la mejor práctica, así que es un poco de información útil, gracias. Estoy de acuerdo con la mayoría de pep8, aunque todavía no es perfecto.
Zelphir Kaltstahl el
1
@Zelphir, sí, a eso me refería. Creo que el término importaciones absolutas en Python siempre se refiere a "proyecto absoluto". Ver: python.org/dev/peps/pep-0328/#rationale-for-absolute-imports . De hecho, estoy bastante seguro de que no puede importar desde ubicaciones de rutas absolutas y aleatorias, al menos utilizando el mecanismo de "importación" predeterminado.
Apteryx
44
He agregado __init__.pyen las pruebas, que resolvió el problema. Ahora puedo usarpytest
Kiran Kumar Kotari
154

conftest solución

La solución menos invasiva es agregar un archivo vacío nombrado conftest.pyen el repo/directorio:

$ touch repo/conftest.py

Eso es. No es necesario escribir código personalizado para destruir sys.patho recordar arrastrar PYTHONPATHo colocar __init__.pyen directorios donde no pertenece.

El directorio del proyecto después:

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

Explicación

pytestbusca los conftestmódulos en la colección de prueba para recopilar ganchos y accesorios personalizados, y para importar los objetos personalizados desde ellos, pytestagrega el directorio principal conftest.pydelsys.path (en este caso, el repodirectorio).

Otras estructuras de proyecto

Si tiene otra estructura de proyecto, coloque el conftest.pydirectorio raíz del paquete (el que contiene paquetes pero no es un paquete en sí mismo, por lo que no contiene un __init__.py), por ejemplo:

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

src diseño

Aunque este enfoque se puede usar con el srcdiseño (colocar conftest.pyen el srcdirectorio):

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

ten en cuenta que la adición srcde PYTHONPATHmitiga el significado y las ventajas del srcdiseño! Terminará probando el código del repositorio y no el paquete instalado. Si necesita hacerlo, tal vez no necesite el srcdirectorio en absoluto.

A dónde ir desde aquí

Por supuesto, los conftestmódulos no son solo algunos archivos para ayudar al descubrimiento del código fuente; es donde pytestocurren todas las mejoras específicas del proyecto del marco y la personalización de su conjunto de pruebas. pytesttiene mucha información sobre conftestmódulos dispersos en sus documentos ; comenzar con conftest.py: complementos locales por directorio

Además, SO tiene una excelente pregunta sobre los conftestmódulos: en py.test, ¿de qué sirven los archivos conftest.py?

hoefling
fuente
2
@ aaa90210 aunque no puedo reproducir su problema (importar desde un conflicto en un directorio raíz funciona en cualquier nivel), nunca debe importar desde archivos de conflicto ya que es un nombre reservado pytesty se recomienda encarecidamente que no lo haga. Al hacer eso, siembras semillas para errores futuros. Cree otro módulo llamado utils.pyy coloque el código para reutilizar en las pruebas allí.
hoefling
44
¡Pulgares hacia arriba! Esta es la única solución que funciona bien para mí.
130nrd
44
absolutamente debe ser la respuesta aceptada - ¡gracias!
Martin Peck
2
Lógicamente, conftest.pyno pertenece al código de la aplicación e, imo, colocarlo debajo src/no es correcto.
Nik O'Lai
2
Esta respuesta debe ser un encabezado fijo en SO. Muchas gracias.
Rigoberta Raviolini
119

Yo tuve el mismo problema. Lo arreglé agregando un __init__.pyarchivo vacío a mi testsdirectorio.

Aron Curzon
fuente
77
Tenga en cuenta que esto no es recomendado por py.test: 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
Vine aquí con la misma pregunta y encontré que eliminarlo __init__.pyde mi directorio de pruebas me lo resolvió.
101
3
@mafro, ¿no veo el problema? Las pruebas no tienen que ser código importable, las encuentra su corredor de pruebas. Solo el código que DEBE PROBARSE debe ser un paquete / módulo instalado, no las pruebas.
K.-Michael Aye
55
Agregar un __init__.pysubdirectorio de test/hace que la importación absoluta funcione para ejecutar pruebas específicas en ese subdirectorio contra los módulos que se instalarán. Gracias.
Bryce Guinta
77
ahí lo tienes: doc.pytest.org/en/latest/goodpractices.html realmente fácil de encontrar con google.
K.-Michael Aye
46

Ejecutarse pytestcomo un módulo con: python -m pytest tests

Stefano Messina
fuente
3
Esto parece ser una solución funcional, pero ¿alguien puede explicar POR QUÉ? Prefiero arreglar la causa subyacente que usarla python -m pytestsin otra explicación que no sea "porque funciona"
Janne Enberg
44
Esto sucede cuando la jerarquía del proyecto es, por ejemplo, package/src package/testsy al testsimportar desde src. La ejecución como módulo considerará las importaciones como absolutas que relativas a la ubicación de ejecución.
Stefano Messina
1
Esta solución me ayudó, gracias! La causa de esto fue por el conflicto en la versión de Python. La prueba pytest funciona para la versión anterior de python. En mi situación, mi versión de python es 3.7.1, python -m pytest tests funciona pero no pytest tests.
Ruxi Zhang
1
Desde Pytest "Ejecutar pytest con python -m pytest [...] en lugar de pytest [...] produce un comportamiento casi equivalente, excepto que la llamada anterior agregará el directorio actual a sys.path".
Moad Ennagi
37

Puede ejecutar con PYTHONPATH en la raíz del proyecto

PYTHONPATH=. py.test

O use pip install como importación editable

pip install -e .   # install package using setup.py in editable mode
Ford Guo
fuente
3
Eso no funcionó para mí con un testdirectorio que no está en la srcestructura del directorio y que llama desde el directorio que contiene ambos testy el srcdirectorio.
Zelphir Kaltstahl el
21

Creé esto como una respuesta a su pregunta y mi propia confusión. Espero que ayude. Presta atención a PYTHONPATH tanto en la línea de comando py.test como en tox.ini.

https://github.com/jeffmacdonald/pytest_test

Específicamente: Tienes que decirle a py.test y tox dónde encontrar los módulos que estás incluyendo.

Con py.test puedes hacer esto:

PYTHONPATH=. py.test

Y con tox, agregue esto a su tox.ini:

[testenv]
deps= -r{toxinidir}/requirements.txt
commands=py.test
setenv =
    PYTHONPATH = {toxinidir}
Jeff MacDonald
fuente
1
¿Podría dar una breve explicación del proyecto que vinculó?
JF Meier
1
Tal vez soy solo yo, pero el archivo README en el proyecto es bastante detallado, y mi comentario en stackoverflow dice por qué creé el repositorio.
Jeff MacDonald
55
Si bien no es estrictamente necesario, es una política habitual tener el contenido principal de una respuesta en la propia respuesta, ya que garantiza que la respuesta sea comprensible en x años a partir de ahora, cuando el recurso vinculado puede haber desaparecido hace mucho tiempo.
JF Meier
:) Oh bien. Eso es internet para ti.
Jeff MacDonald
9

Tuve el mismo problema en Flask.

Cuando agregué:

__init__.py

a la carpeta de pruebas, el problema desapareció :)

Probablemente la aplicación no pudo reconocer las pruebas de carpeta como módulo

usuario13037517
fuente
8

Lo arreglé eliminando el nivel superior __init__.pyen la carpeta principal de mis fuentes.

Gonzalo
fuente
1
Me lo arregló. ¿Alguien puede explicar esto?
aboger
esto aquí mismo me lo arregló también. Definitivamente me encantaría ver una explicación para esto si alguien lo tiene
king_wayne
agregué init .py pero aún enfrentaba los mismos problemas, pero esta solución también funcionó para mí ... ¿por qué?
Abhijit
7

Comencé a recibir ConftestImportFailure: ImportError('No module named ...errores extraños cuando accidentalmente agregué un __init__.pyarchivo a mi directorio src (que no se suponía que fuera un paquete de Python, solo un contenedor de todas las fuentes).

jbasko
fuente
3

Estaba recibiendo este error debido a algo aún más simple (incluso podría decirse trivial). No había instalado el pytestmódulo. Así que un simple lo apt install python-pytestarregló para mí.

'pytest' habría sido incluido en setup.py como una dependencia de prueba. Asegúrese de instalar también los requisitos de prueba.

craq
fuente
3

Tuve un problema similar pytestno reconoció un módulo instalado en el entorno en el que estaba trabajando.

Lo resolví instalando también pytesten el mismo entorno.

nocibambi
fuente
Aunque estaba usando pytest desde un venv, también lo instalé globalmente, lo que me dio este error. Después de desinstalar la versión global e instalar dentro del venv, funcionó.
Markus Ressel
2

Para mí, el problema fue tests.pygenerado por Django junto con el testsdirectorio. La eliminación tests.pysolucionó el problema.

Paweł Mucha
fuente
2

Recibí este error porque utilicé importaciones relativas incorrectamente. En el ejemplo de OP, test_app.py debería importar funciones usando, por ejemplo,

from repo.app import *

Sin embargo, los archivos generosamente __init__.py están dispersos alrededor de la estructura del archivo, esto no funciona y crea el tipo de ImportError visto a menos que los archivos y los archivos de prueba estén en el mismo directorio.

from app import *

Aquí hay un ejemplo de lo que tuve que hacer con uno de mis proyectos:

Aquí está mi estructura de proyecto:

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

Para poder acceder a activity_indicator.py desde test_activity_indicator.py, necesitaba:

  • Inicie test_activity_indicatory.py con la importación relativa correcta:
    from microbit.activity_indicator.activity_indicator import *
  • poner archivos __init__.py en toda la estructura del proyecto:
    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
fuente
0

Muy a menudo, las pruebas se interrumpían debido a que el módulo no se podía importar. Después de investigar, descubrí que el sistema está mirando el archivo en el lugar incorrecto y podemos superar fácilmente el problema copiando el archivo que contiene el módulo, en la misma carpeta que se indicó, para que se importe correctamente. Otra propuesta de solución sería cambiar la declaración para la importación y mostrar a MutPy la ruta correcta de la unidad. Sin embargo, debido al hecho de que varias unidades pueden tener esta dependencia, lo que significa que debemos confirmar los cambios también en sus declaraciones, preferimos simplemente mover la unidad a la carpeta.

Baydaa
fuente
Hay otras respuestas que proporcionan la pregunta del OP, y se publicaron hace algún tiempo. Cuando publique una respuesta, asegúrese de agregar una nueva solución o una explicación sustancialmente mejor, especialmente al responder preguntas anteriores. A veces es mejor publicar un comentario sobre una respuesta en particular.
help-info.de
Además del comentario de @ help-info.de: Aquí está el enlace a la guía para responder preguntas: stackoverflow.com/help/how-to-answer
la mano de NOD
0

Según una publicación en Medium de Dirk Avery (y respaldada por mi experiencia personal), si está utilizando un entorno virtual para su proyecto, entonces no puede utilizar una instalación de pytest en todo el sistema; tienes que instalarlo en el entorno virtual y usar esa instalación.

En particular, si lo tiene instalado en ambos lugares, simplemente ejecutar el pytestcomando no funcionará porque usará la instalación del sistema. Como han descrito las otras respuestas, una solución simple es ejecutar en python -m pytestlugar de pytest; esto funciona porque usa la versión del entorno de pytest. Alternativamente, puede desinstalar la versión del sistema de pytest; Después de reactivar el entorno virtual, el pytestcomando debería funcionar.

Einhaender
fuente
Lo único que funcionó para mí hasta ahora fue python -m pytest tests/.
Nik O'Lai
0

Estaba teniendo el mismo problema al seguir el tutorial de Flask y encontré la respuesta en los documentos oficiales de Pytest. Es un pequeño cambio con respecto a la forma en que yo (y creo que muchos otros) están acostumbrados a hacer las cosas.

Debe crear un setup.pyarchivo en el directorio raíz de su proyecto con al menos las dos líneas siguientes:

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

donde PACKAGENAME es el nombre de tu aplicación. Luego tienes que instalarlo con pip:

pip install -e .

La -ebandera le dice a pip que instale el paquete en modo editable o "desarrollo". Por lo tanto, la próxima vez que lo ejecute pytestdebería encontrar su aplicación en el estándar PYTHONPATH.

Luis Lezcano Airaldi
fuente
0

Mi solución:

cree el conftest.pyarchivo en el testdirectorio que contiene:

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

Esto agregará la carpeta de interés a la ruta de Python sin modificar cada archivo de prueba , configurando la variable env o jugando con rutas absolutas / relativas.

davide burba
fuente