tests - Python: ¿Cómo ejecutar unittest.main() para todos los archivos fuente en un subdirectorio?
python unittest skipif (6)
A partir de Python 2.7, el descubrimiento de prueba está automatizado en el paquete unittest. De los unittest :
Unittest es compatible con el descubrimiento de prueba simple. Para ser compatible con el descubrimiento de prueba, todos los archivos de prueba deben ser módulos o paquetes importables desde el directorio de nivel superior del proyecto (esto significa que sus nombres de archivo deben ser identificadores válidos).
El descubrimiento de prueba se implementa en
TestLoader.discover()
, pero también se puede usar desde la línea de comando. El uso básico de la línea de comandos es:
cd project_directory python -m unittest discover
Por defecto, busca paquetes llamados test*.py
, pero esto se puede cambiar para que pueda usar algo como
python -m unittest discover --pattern=*.py
En lugar de tu script test.py
Estoy desarrollando un módulo de Python con varios archivos fuente, cada uno con su propia clase de prueba derivada de unittest directamente en la fuente. Considere la estructura del directorio:
dirFoo/
test.py
dirBar/
__init__.py
Foo.py
Bar.py
Para probar Foo.py o Bar.py, agregaría esto al final de los archivos fuente de Foo.py y Bar.py:
if __name__ == "__main__":
unittest.main()
Y ejecute Python en cualquier fuente, es decir,
$ python Foo.py
...........
----------------------------------------------------------------------
Ran 11 tests in 2.314s
OK
Idealmente, haría que "test.py" busque automágicamente dirBar para cualquier clase derivada de prueba de unidad y realice una llamada a "unittest.main ()". ¿Cuál es la mejor manera de hacer esto en la práctica?
Intenté usar Python para llamar a execfile para cada archivo * .py en dirBar, que se ejecuta una vez para el primer archivo .py encontrado y sale de la llamada test.py, y luego tengo que duplicar mi código agregando unittest.main () en cada archivo fuente - que viola los principios DRY.
Aquí está mi código de descubrimiento de prueba que parece hacer el trabajo. Quería asegurarme de poder extender las pruebas fácilmente sin tener que enumerarlas en ninguno de los archivos involucrados, pero también evitar escribir todas las pruebas en un solo archivo Übertest.
Entonces la estructura es
myTests.py
testDir/
__init__.py
testA.py
testB.py
myTest.py se ve así:
import unittest
if __name__ == ''__main__'':
testsuite = unittest.TestLoader().discover(''.'')
unittest.TextTestRunner(verbosity=1).run(testsuite)
Creo que esta es la solución más simple para escribir varios casos de prueba en un directorio. La solución requiere Python 2.7 o Python 3.
Deberías probar con la nose . Es una biblioteca para ayudar a crear pruebas y se integra con unittest
o doctest
. Todo lo que necesita hacer es ejecutar nosetests
y encontrará todas sus nosetests
por usted.
% nosetests # finds all tests in all subdirectories
% nosetests tests/ # find all tests in the tests directory
En caso de que ayudara a alguien, aquí está el enfoque al que llegué para resolver este problema. Tuve el caso de uso donde tengo la siguiente estructura de directorio:
mypackage/
tests/
test_category_1/
tests_1a.py
tests_1b.py
...
test_category_2/
tests_2a.py
tests_2b.py
...
...
y quiero que todo lo siguiente funcione de la manera más obvia y que se me puedan proporcionar los mismos argumentos de línea de comandos aceptados por unittest:
python -m mypackage.tests
python -m mypackage.tests.test_category_1
python -m mypackage.tests.test_category_1.tests_1a
La solución fue configurar mypackage/tests/__init__.py
esta manera:
import unittest
def prepare_load_tests_function (the__path__):
test_suite = unittest.TestLoader().discover(the__path__[0])
def load_tests (_a, _b, _c):
return test_suite
return load_tests
y para configurar mypackage/tests/__main__.py
esta manera:
import unittest
from . import prepare_load_tests_function, __path__
load_tests = prepare_load_tests_function(__path__)
unittest.main()
y copiar y pegar un __init__.py
vacío y el siguiente __main__.py
en cada mypackage/tests/test_category_n/
:
import unittest
from .. import prepare_load_tests_function
from . import __path__
load_tests = prepare_load_tests_function(__path__)
unittest.main()
y también para agregar el estándar if __name__ == ''__main__'': unittest.main()
en cada archivo de prueba real.
(Funciona para mí en Python 3.3 en Windows, ymmv.)
Sabía que había una solución obvia:
dirFoo/
__init__.py
test.py
dirBar/
__init__.py
Foo.py
Bar.py
Contenido de dirFoo / test.py
from dirBar import *
import unittest
if __name__ == "__main__":
unittest.main()
Ejecute las pruebas:
$ python test.py
...........
----------------------------------------------------------------------
Ran 11 tests in 2.305s
OK
Perdón por la pregunta tonta.
Se me ocurrió un fragmento que puede hacer lo que quieras. Recorre un camino que proporciona en busca de paquetes / módulos de Python y acumula un conjunto de suites de prueba de esos módulos, que luego ejecuta de una sola vez.
Lo bueno de esto es que funcionará en todos los paquetes anidados en el directorio que especifique, y no tendrá que cambiar manualmente las importaciones a medida que agrega nuevos componentes.
import logging
import os
import unittest
MODULE_EXTENSIONS = set(''.py .pyc .pyo''.split())
def unit_test_extractor(tup, path, filenames):
"""Pull ``unittest.TestSuite``s from modules in path
if the path represents a valid Python package. Accumulate
results in `tup[1]`.
"""
package_path, suites = tup
logging.debug(''Path: %s'', path)
logging.debug(''Filenames: %s'', filenames)
relpath = os.path.relpath(path, package_path)
relpath_pieces = relpath.split(os.sep)
if relpath_pieces[0] == ''.'': # Base directory.
relpath_pieces.pop(0) # Otherwise, screws up module name.
elif not any(os.path.exists(os.path.join(path, ''__init__'' + ext))
for ext in MODULE_EXTENSIONS):
return # Not a package directory and not the base directory, reject.
logging.info(''Base: %s'', ''.''.join(relpath_pieces))
for filename in filenames:
base, ext = os.path.splitext(filename)
if ext not in MODULE_EXTENSIONS: # Not a Python module.
continue
logging.info(''Module: %s'', base)
module_name = ''.''.join(relpath_pieces + [base])
logging.info(''Importing from %s'', module_name)
module = __import__(module_name)
module_suites = unittest.defaultTestLoader.loadTestsFromModule(module)
logging.info(''Got suites: %s'', module_suites)
suites += module_suites
def get_test_suites(path):
""":return: Iterable of suites for the packages/modules
present under :param:`path`.
"""
logging.info(''Base path: %s'', package_path)
suites = []
os.path.walk(package_path, unit_test_extractor, (package_path, suites))
logging.info(''Got suites: %s'', suites)
return suites
if __name__ == ''__main__'':
logging.basicConfig(level=logging.WARN)
package_path = os.path.dirname(os.path.abspath(__file__))
suites = get_test_suites(package_path)
for suite in suites:
unittest.TextTestRunner(verbosity=2).run(suite)