skipif - unittest python 3
Ejecución de unittest con la estructura de directorio de prueba típica (18)
La estructura de directorio muy común incluso para un módulo Python simple parece ser separar las pruebas de unidad en su propio directorio de test
:
new_project/
antigravity/
antigravity.py
test/
test_antigravity.py
setup.py
etc.
por ejemplo, ver este proyecto de Python howto .
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 puede simplemente ejecutar python test_antigravity.py
desde el directorio de prueba, ya que su import antigravity
fallará ya que 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 usted es el desarrollador, pero no es realista esperar que los usuarios los usen si solo quieren comprobar que las pruebas son paso.
La otra alternativa es simplemente copiar el archivo de prueba en el otro directorio, pero parece un poco tonto y pierde el punto de tenerlos en un directorio separado para comenzar.
Entonces, si acabas de descargar la fuente a mi nuevo proyecto, ¿cómo ejecutarías las pruebas unitarias? Prefiero una respuesta que me permita decirles a mis usuarios: "Para ejecutar las pruebas unitarias, haga X".
¿Cuál es la forma habitual de ejecutar las pruebas?
Yo uso Python 3.6.2
cd new_project
pytest test/test_antigravity.py
Para instalar pytest : sudo pip install pytest
No establecí ninguna variable de ruta y mis importaciones no están fallando con la misma estructura de proyecto de "prueba".
if __name__ == ''__main__''
esto: if __name__ == ''__main__''
así:
test_antigravity.py
import antigravity
class TestAntigravity(unittest.TestCase):
def test_something(self):
# ... test stuff here
# if __name__ == ''__main__'':
#
# if __package__ is None:
#
# import something
# sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
# from .. import antigravity
#
# else:
#
# from .. import antigravity
#
# unittest.main()
Python 3+
Añadiendo a @Pierre
Usando la estructura de directorios de unittest
esta manera:
new_project
├── antigravity
│ ├── __init__.py # make it a package
│ └── antigravity.py
└── test
├── __init__.py # also make test a package
└── test_antigravity.py
Para ejecutar el módulo de prueba test_antigravity.py
:
$ cd new_project
$ python -m unittest test.test_antigravity
O un solo TestCase
$ python -m unittest test.test_antigravity.GravityTestCase
Obligatorio , no olvide el __init__.py
incluso si está vacío, de lo contrario no funcionará.
Del artículo al que has vinculado:
Cree un archivo test_modulename.py y ponga sus pruebas de prueba de unidad en él. Dado que los módulos de prueba están en un directorio separado de su código, es posible que deba agregar el directorio principal de su módulo a su PYTHONPATH para ejecutarlos:
$ cd /path/to/googlemaps $ export PYTHONPATH=$PYTHONPATH:/path/to/googlemaps/googlemaps $ python test/test_googlemaps.py
Finalmente, hay un marco de prueba de unidades más popular para Python (¡es tan importante!), La nariz. nose ayuda a simplificar y extender el marco de prueba unitario integrado (por ejemplo, puede encontrar automáticamente su código de prueba y configurar PYTHONPATH para usted), pero no está incluido en la distribución estándar de Python.
Tal vez debería mirar a la nose como sugiere?
Es posible usar una envoltura que ejecuta seleccionados o todas las pruebas.
Por ejemplo:
./run_tests antigravity/*.py
o para ejecutar todas las pruebas de forma recursiva utilice globbing ( tests/**/*.py
) (habilitado por shopt -s globstar
).
Básicamente, el contenedor puede usar argparse
para analizar los argumentos como:
parser = argparse.ArgumentParser()
parser.add_argument(''files'', nargs=''*'')
Luego carga todas las pruebas:
for filename in args.files:
exec(open(filename).read())
Luego agréguelos a su conjunto de pruebas (utilizando inspect
)
alltests = unittest.TestSuite()
for name, obj in inspect.getmembers(sys.modules[__name__]):
if inspect.isclass(obj) and name.startswith("FooTest"):
alltests.addTest(unittest.makeSuite(obj))
y ejecútalos
result = unittest.TextTestRunner(verbosity=2).run(alltests)
Revisa this ejemplo para más detalles.
Ver también: ¿Cómo ejecutar todas las pruebas unitarias de Python en un directorio?
Esta secuencia de comandos BASH ejecutará el directorio de prueba de prueba de python desde cualquier lugar del sistema de archivos, sin importar en qué directorio de trabajo se encuentre.
Esto es útil cuando se mantiene en el directorio de trabajo ./src
o ./example
y necesita una prueba de unidad rápida:
#!/bin/bash
this_program="$0"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"
python -m unittest discover -s "$readlink"/test -v
No es necesario un archivo de test/__init__.py
para cargar su paquete / sobrecarga de memoria durante la producción.
Generalmente, creo un script de "ejecutar pruebas" en el directorio del proyecto (el que es común tanto para el directorio de origen como para la test
) que carga mi paquete de "Todas las pruebas". Este suele ser el código de repetición, 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 (en ¿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 incluso include antigravity
en sus módulos de prueba. El inconveniente es que necesitaría más código de soporte para ejecutar una prueba en particular ... simplemente los ejecuto todos cada vez.
La mejor solución en mi opinión es usar la interfaz de línea de comandos de unittest
que agregará el directorio a sys.path
para que no tenga que hacerlo (hecho en la clase TestLoader
).
Por ejemplo, para una estructura de directorio como esta:
new_project
├── antigravity.py
└── test_antigravity.py
Usted puede simplemente ejecutar:
$ cd new_project
$ python -m unittest test_antigravity
Para una estructura de directorios como la tuya:
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 paquete de test
, puede importar el paquete antigravity
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 solo módulo de prueba:
Para ejecutar un solo 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.
Ejecutando un solo caso de prueba o método de prueba:
También puede ejecutar un único TestCase
o un solo método de prueba:
$ python -m unittest test.test_antigravity.GravityTestCase
$ python -m unittest test.test_antigravity.GravityTestCase.test_method
Ejecutando todas las pruebas:
También puede usar el descubrimiento de prueba que descubrirá y ejecutará todas las pruebas por usted, deben ser módulos o paquetes llamados test*.py
(se pueden cambiar con el indicador -p, --pattern
):
$ cd new_project
$ python -m unittest discover
Esto ejecutará todos los módulos de test*.py
dentro del paquete de test
.
La siguiente es la estructura de mi proyecto:
ProjectFolder:
- project:
- __init__.py
- item.py
- tests:
- test_item.py
Me pareció mejor importar en el método setUp ():
import unittest
import sys
class ItemTest(unittest.TestCase):
def setUp(self):
sys.path.insert(0, "../project")
from project import item
# further setup using this import
def test_item_props(self):
# do my assertions
if __name__ == "__main__":
unittest.main()
La solución más sencilla para sus usuarios es proporcionar una secuencia de comandos ejecutable ( runtests.py
o algo runtests.py
) que reinicie el entorno de prueba necesario, incluido, si es necesario, agregar temporalmente su directorio raíz del proyecto a sys.path. Esto no requiere que los usuarios establezcan variables de entorno, algo como esto funciona bien en una secuencia de comandos de arranque:
import sys, os
sys.path.insert(0, os.path.dirname(__file__))
Luego, sus instrucciones para sus usuarios pueden ser tan simples como "python runtests.py".
Por supuesto, si la ruta que necesita realmente es os.path.dirname(__file__)
, entonces no necesita agregarla a sys.path
en absoluto; Python siempre coloca el directorio de la secuencia de comandos que se ejecuta actualmente al comienzo de sys.path
, por lo que, dependiendo de la estructura de su directorio, basta con ubicar su runtests.py
en el lugar correcto para que sea todo lo que necesite.
Además, el módulo unittest en Python 2.7+ (que está respaldado como unittest2 para Python 2.6 y versiones anteriores) ahora tiene incorporado el descubrimiento de pruebas , por lo que no es necesario continuar con la detección de pruebas automatizada: sus instrucciones de usuario pueden ser tan simples como "Python -m unittest discover".
No puedes importar desde el directorio padre sin un poco de vudú. Aquí hay otra manera que funciona con al menos Python 3.6.
Primero, tenga un archivo test / context.py con el siguiente contenido:
import sys
import os
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ''..'')))
Luego tenga la siguiente importación en el archivo test / test_antigravity.py:
import unittest
try:
import context
except ModuleNotFoundError:
import test.context
import antigravity
Tenga en cuenta que el motivo de esta cláusula try-except es que
- import test.context falla cuando se ejecuta con "python test_antigravity.py" y
- el contexto de importación falla cuando se ejecuta con "python -m unittest" del directorio new_project.
Con este engaño ambos trabajan.
Ahora puede ejecutar todos los archivos de prueba dentro del directorio de prueba con:
$ pwd
/projects/new_project
$ python -m unittest
o ejecute un archivo de prueba individual con:
$ cd test
$ python test_antigravity
Ok, no es mucho más bonito que tener el contenido de context.py dentro de test_antigravity.py, pero tal vez un poco. Las sugerencias son bienvenidas.
Noté que si ejecuta la interfaz de línea de comandos de unittest desde su directorio "src", las importaciones funcionarán correctamente sin modificaciones.
python -m unittest discover -s ../test
Si desea poner eso en un archivo por lotes en el directorio de su proyecto, puede hacer esto:
setlocal & cd src & python -m unittest discover -s ../test
Realmente deberías usar la herramienta pip.
Use pip install -e
para instalar su paquete en modo de desarrollo. Esta es una muy buena práctica.
En la URL de referencia que se proporciona a continuación, se proporcionan 2 diseños de proyectos clásicos (con prueba), puede seguir cualquiera de ellos.
Ref :
Si tiene varios directorios en su directorio de prueba, entonces debe agregar a cada directorio un archivo __init__.py
.
/home/johndoe/snakeoil
└── test
├── __init__.py
└── frontend
└── __init__.py
└── test_foo.py
└── backend
└── __init__.py
└── test_bar.py
Luego, para ejecutar cada prueba a la vez, ejecute:
python -m unittest discover -s /home/johndoe/snakeoil/test -t /home/johndoe/snakeoil
Fuente: python -m unittest -h
-s START, --start-directory START
Directory to start discovery (''.'' default)
-t TOP, --top-level-directory TOP
Top level directory of project (defaults to start
directory)
Si usa el Código VS y sus pruebas se ubican en el mismo nivel que su proyecto, la ejecución y depuración del código no funcionarán de manera inmediata. Lo que puedes hacer es cambiar tu archivo launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Python",
"type": "python",
"request": "launch",
"stopOnEntry": false,
"pythonPath": "${config:python.pythonPath}",
"program": "${file}",
"cwd": "${workspaceRoot}",
"env": {},
"envFile": "${workspaceRoot}/.env",
"debugOptions": [
"WaitOnAbnormalExit",
"WaitOnNormalExit",
"RedirectOutput"
]
}
]
}
La línea clave aquí es envFile
"envFile": "${workspaceRoot}/.env",
En la raíz de su proyecto agregue el archivo .env
Dentro de su archivo .env agregue una ruta a la raíz de su proyecto. Esto agregará temporalmente
PYTHONPATH = C: / YOUR / PYTHON / PROJECT / ROOT_DIRECTORY
ruta a su proyecto y podrá utilizar las pruebas de unidad de depuración de VS Code
Tuve el mismo problema, con una carpeta de pruebas de unidad separada. De las sugerencias mencionadas, agrego la ruta de origen absoluta a sys.path
.
El beneficio de la siguiente solución es que uno puede ejecutar el archivo test/test_yourmodule.py
sin cambiar primero al directorio de prueba:
import sys, os
testdir = os.path.dirname(__file__)
srcdir = ''../antigravity''
sys.path.insert(0, os.path.abspath(os.path.join(testdir, srcdir)))
import antigravity
import unittest
Use setup.py develop
para hacer que su directorio de trabajo sea parte del entorno de Python instalado, luego ejecute las pruebas.
si ejecuta "python setup.py develop", el paquete estará en la ruta. Pero es posible que no desee hacerlo porque podría infectar la instalación de Python en su sistema, por lo que existen herramientas como virtualenv y buildout .
Solución / Ejemplo para el módulo unitario de Python
Dada la siguiente estructura de proyecto:
ProjectName
├── project_name
| ├── models
| | └── thing_1.py
| └── __main__.py
└── test
├── models
| └── test_thing_1.py
└── __main__.py
Puede ejecutar su proyecto desde el directorio raíz con python project_name
, que llama a ProjectName/project_name/__main__.py
.
Para ejecutar sus pruebas con la python test
, ejecutando efectivamente ProjectName/test/__main__.py
, debe hacer lo siguiente:
1) Convierta su directorio de test/models
en un paquete agregando un archivo __init__.py
. Esto hace que los casos de prueba dentro del subdirectorio sean accesibles desde el directorio de test
principal.
# ProjectName/test/models/__init__.py
from .test_thing_1 import Thing1TestCase
2) Modifique la ruta de su sistema en test/__main__.py
para incluir el directorio project_name
.
# ProjectName/test/__main__.py
import sys
import unittest
sys.path.append(''../project_name'')
loader = unittest.TestLoader()
testSuite = loader.discover(''test'')
testRunner = unittest.TextTestRunner(verbosity=2)
testRunner.run(testSuite)
Ahora puede importar con éxito cosas de project_name
en sus pruebas.
# ProjectName/test/models/test_thing_1.py
import unittest
from project_name.models import Thing1 # this doesn''t work without ''sys.path.append'' per step 2 above
class Thing1TestCase(unittest.TestCase):
def test_thing_1_init(self):
thing_id = ''ABC''
thing1 = Thing1(thing_id)
self.assertEqual(thing_id, thing.id)