Ejecución de unittest con estructura de directorio de prueba típica
La estructura de directorios muy común incluso para un módulo de Python simple parece ser separar las pruebas unitarias en su propio test
directorio:
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.py
desde el directorio de prueba ya que import antigravity
fallará 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".
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.path
para que usted no tenga que hacerlo (hecho en la TestLoader
clase).
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 test
paquete, puedes importar el antigravity
paquete 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 TestCase
mé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, --pattern
bandera):
$ 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*.py
módulos dentro del test
paquete.
Aquí puede encontrar la documentación oficial actualizada de discovery
.
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__.py
script 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.
La solución más sencilla para sus usuarios es proporcionar un script ejecutable ( runtests.py
o algo similar) que inicie el entorno de prueba necesario, incluido, si es necesario, agregar el directorio raíz de su proyecto sys.path
temporalmente. 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.path
absoluto; 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.py
en 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
.
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 antigravity
en 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.
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?