Ejecución de unittest con estructura de directorio de prueba típica

Resuelto Major Major asked hace 14 años • 27 respuestas

La estructura de directorios muy común incluso para un módulo de Python simple parece ser separar las pruebas unitarias en su propio testdirectorio:

new_project/
    antigravity/
        antigravity.py
    test/
        test_antigravity.py
    setup.py
    etc.

Mi pregunta es simplemente ¿Cuál es la forma habitual de ejecutar las pruebas? Sospecho que esto es obvio para todos excepto para mí, pero no se puede simplemente ejecutar python test_antigravity.pydesde el directorio de prueba ya que import antigravityfallará porque el módulo no está en la ruta.

Sé que podría modificar PYTHONPATH y otros trucos relacionados con la ruta de búsqueda, pero no puedo creer que sea la forma más sencilla: está bien si eres el desarrollador, pero no es realista esperar que tus usuarios lo utilicen si solo quieren comprobar que las pruebas están paso.

La otra alternativa es simplemente copiar el archivo de prueba en el otro directorio, pero parece un poco tonto y no tiene sentido tenerlos en un directorio separado para empezar.

Entonces, si acabaras de descargar el código fuente de mi nuevo proyecto, ¿cómo ejecutarías las pruebas unitarias? Preferiría una respuesta que me permitiera decirles a mis usuarios: "Para ejecutar las pruebas unitarias, haga X".

Major Major avatar Dec 13 '09 23:12 Major Major
Aceptado

En mi opinión, la mejor solución es utilizar la unittest interfaz de línea de comandos que agregará el directorio al directorio sys.pathpara que usted no tenga que hacerlo (hecho en la TestLoaderclase).

Por ejemplo, para una estructura de directorio como esta:

new_project
├── antigravity.py
└── test_antigravity.py

Puedes simplemente ejecutar:

$ cd new_project
$ python -m unittest test_antigravity

Para una estructura de directorio como la suya:

new_project
├── antigravity
│   ├── __init__.py         # make it a package
│   └── antigravity.py
└── test
    ├── __init__.py         # also make test a package
    └── test_antigravity.py

Y en los módulos de prueba dentro del testpaquete, puedes importar el antigravitypaquete y sus módulos como de costumbre:

# import the package
import antigravity

# import the antigravity module
from antigravity import antigravity

# or an object inside the antigravity module
from antigravity.antigravity import my_object

Ejecutando un único módulo de prueba:

Para ejecutar un único módulo de prueba, en este caso test_antigravity.py:

$ cd new_project
$ python -m unittest test.test_antigravity

Simplemente haga referencia al módulo de prueba de la misma manera que lo importa.

Ejecutar un único caso de prueba o método de prueba:

También puede ejecutar un TestCasemétodo de prueba único o único:

$ python -m unittest test.test_antigravity.GravityTestCase
$ python -m unittest test.test_antigravity.GravityTestCase.test_method

Ejecutando todas las pruebas:

También puede utilizar el descubrimiento de pruebas , que descubrirá y ejecutará todas las pruebas por usted; deben ser módulos o paquetes con el nombre test*.py(se puede cambiar con la -p, --patternbandera):

$ cd new_project
$ python -m unittest discover
$ # Also works without discover for Python 3
$ # as suggested by @Burrito in the comments
$ python -m unittest

Esto ejecutará todos los test*.pymódulos dentro del testpaquete.

Aquí puede encontrar la documentación oficial actualizada de discovery.

Pierre avatar Jun 17 '2014 14:06 Pierre

Hace tiempo que tengo el mismo problema. Lo que elegí recientemente es la siguiente estructura de directorios:

project_path
├── Makefile
├── src
│   ├── script_1.py
│   ├── script_2.py
│   └── script_3.py
└── tests
    ├── __init__.py
    ├── test_script_1.py
    ├── test_script_2.py
    └── test_script_3.py

y en el __init__.pyscript de la carpeta de prueba escribo lo siguiente:

import os
import sys
PROJECT_PATH = os.getcwd()
SOURCE_PATH = os.path.join(
    PROJECT_PATH,"src"
)
sys.path.append(SOURCE_PATH)

Muy importante para compartir el proyecto es el Makefile, porque obliga a ejecutar los scripts correctamente. Aquí está el comando que puse en el Makefile:

run_tests:
    python -m unittest discover .

El Makefile es importante no sólo por el comando que ejecuta sino también por el lugar desde donde lo ejecuta . Si hiciera cd in tests y do python -m unittest discover ., no funcionaría porque el script de inicio en unit_tests llama a os.getcwd(), lo que luego apuntaría a la ruta absoluta incorrecta (que se agregaría a sys.path y faltaría su carpeta de origen). Los scripts se ejecutarían ya que Discover encuentra todas las pruebas, pero no se ejecutarían correctamente. Entonces el Makefile está ahí para evitar tener que recordar este problema.

Realmente me gusta este enfoque porque no tengo que tocar mi carpeta src, mis pruebas unitarias o mis variables de entorno y todo funciona sin problemas.

Patrick Da Silva avatar Jan 14 '2020 11:01 Patrick Da Silva

La solución más sencilla para sus usuarios es proporcionar un script ejecutable ( runtests.pyo algo similar) que inicie el entorno de prueba necesario, incluido, si es necesario, agregar el directorio raíz de su proyecto sys.pathtemporalmente. Esto no requiere que los usuarios establezcan variables de entorno; algo como esto funciona bien en un script de arranque:

import sys, os

sys.path.insert(0, os.path.dirname(__file__))

Entonces sus instrucciones para sus usuarios pueden ser tan simples como " python runtests.py".

Por supuesto, si la ruta que realmente necesita es os.path.dirname(__file__), entonces no necesita agregarla en sys.pathabsoluto; Python siempre coloca el directorio del script que se está ejecutando actualmente al principio de sys.path, por lo que dependiendo de la estructura de su directorio, simplemente ubicarlo runtests.pyen el lugar correcto puede ser todo lo que necesita.

Además, el módulo unittest en Python 2.7+ (que está respaldado como unittest2 para Python 2.6 y versiones anteriores) ahora tiene el descubrimiento de pruebas incorporado, por lo que ya no es necesario si desea el descubrimiento de pruebas automatizado: sus instrucciones de usuario pueden ser tan simples como python -m unittest discover.

Carl Meyer avatar Dec 13 '2009 20:12 Carl Meyer

Generalmente creo un script de "ejecutar pruebas" en el directorio del proyecto (el que es común tanto para el directorio fuente como para test) que carga mi conjunto "Todas las pruebas". Suele ser un código repetitivo, por lo que puedo reutilizarlo de un proyecto a otro.

run_tests.py:

import unittest
import test.all_tests
testSuite = test.all_tests.create_test_suite()
text_runner = unittest.TextTestRunner().run(testSuite)

test/all_tests.py (de ¿Cómo ejecuto todas las pruebas unitarias de Python en un directorio? )

import glob
import unittest

def create_test_suite():
    test_file_strings = glob.glob('test/test_*.py')
    module_strings = ['test.'+str[5:len(str)-3] for str in test_file_strings]
    suites = [unittest.defaultTestLoader.loadTestsFromName(name) \
              for name in module_strings]
    testSuite = unittest.TestSuite(suites)
    return testSuite

Con esta configuración, puede hacerlo simplemente include antigravityen sus módulos de prueba. La desventaja es que necesitarías más código de soporte para ejecutar una prueba en particular... Yo simplemente las ejecuto todas cada vez.

stw_dev avatar Jun 07 '2010 19:06 stw_dev

Del artículo al que vinculaste:

Cree un archivo test_modulename.py y coloque sus pruebas unitarias en él. Dado que los módulos de prueba están en un directorio separado de su código, es posible que necesite agregar el directorio principal de su módulo a su PYTHONPATH para poder ejecutarlos:

$ cd /path/to/googlemaps

$ export PYTHONPATH=$PYTHONPATH:/path/to/googlemaps/googlemaps

$ python test/test_googlemaps.py

Finalmente, existe un marco de prueba unitario más popular para Python (¡es así de importante!), nose. nose ayuda a simplificar y ampliar el marco de prueba unitario incorporado (puede, por ejemplo, encontrar automáticamente su código de prueba y configurar su PYTHONPATH por usted), pero no está incluido con la distribución estándar de Python.

¿Quizás deberías mirar la nariz como sugiere?

Mark Byers avatar Dec 13 '2009 16:12 Mark Byers