unittest unit test skipif pruebas not integracion equal python unit-testing testing python-unittest

unit - testing en python



¿Cómo ejecuto todas las pruebas unitarias de Python en un directorio? (14)

Tengo un directorio que contiene mis pruebas unitarias de Python. Cada módulo de prueba de unidad tiene la forma de prueba _ *. Py . Estoy intentando crear un archivo llamado all_test.py que, adivinó, ejecutará todos los archivos en el formulario de prueba mencionado y devolverá el resultado. He intentado dos métodos hasta ahora; ambos han fracasado Mostraré los dos métodos y espero que alguien sepa cómo hacerlo correctamente.

Para mi primer intento valeroso, pensé: "Si solo importo todos mis módulos de prueba en el archivo y luego llamo a esta unittest.main() doodad, funcionará, ¿verdad?" Bueno, resulta que estaba equivocado.

import glob import unittest testSuite = unittest.TestSuite() test_file_strings = glob.glob(''test_*.py'') module_strings = [str[0:len(str)-3] for str in test_file_strings] if __name__ == "__main__": unittest.main()

Esto no funcionó, el resultado que obtuve fue:

$ python all_test.py ---------------------------------------------------------------------- Ran 0 tests in 0.000s OK

Para mi segundo intento, pensé, bueno, tal vez intentaré hacer todo esto de manera más "manual". Así que intenté hacer eso a continuación:

import glob import unittest testSuite = unittest.TestSuite() test_file_strings = glob.glob(''test_*.py'') module_strings = [str[0:len(str)-3] for str in test_file_strings] [__import__(str) for str in module_strings] suites = [unittest.TestLoader().loadTestsFromName(str) for str in module_strings] [testSuite.addTest(suite) for suite in suites] print testSuite result = unittest.TestResult() testSuite.run(result) print result #Ok, at this point I have a result #How do I display it as the normal unit test command line output? if __name__ == "__main__": unittest.main()

Esto tampoco funcionó, pero parece tan cerca!

$ python all_test.py <unittest.TestSuite tests=[<unittest.TestSuite tests=[<unittest.TestSuite tests=[<test_main.TestMain testMethod=test_respondes_to_get>]>]>]> <unittest.TestResult run=1 errors=0 failures=0> ---------------------------------------------------------------------- Ran 0 tests in 0.000s OK

Parece que tengo una suite de algún tipo, y puedo ejecutar el resultado. Estoy un poco preocupado por el hecho de que dice que solo he run=1 , parece que debería run=2 , pero es progreso. Pero, ¿cómo paso y muestro el resultado a main? O, ¿cómo básicamente lo hago funcionar para que pueda ejecutar este archivo y, al hacerlo, ejecutar todas las pruebas de unidad en este directorio?


Basándome en la respuesta de Stephen Cagle , agregué soporte para módulos de prueba anidados.

import fnmatch import os import unittest def all_test_modules(root_dir, pattern): test_file_names = all_files_in(root_dir, pattern) return [path_to_module(str) for str in test_file_names] def all_files_in(root_dir, pattern): matches = [] for root, dirnames, filenames in os.walk(root_dir): for filename in fnmatch.filter(filenames, pattern): matches.append(os.path.join(root, filename)) return matches def path_to_module(py_file): return strip_leading_dots( / replace_slash_by_dot( / strip_extension(py_file))) def strip_extension(py_file): return py_file[0:len(py_file) - len(''.py'')] def replace_slash_by_dot(str): return str.replace(''//', ''.'').replace(''/'', ''.'') def strip_leading_dots(str): while str.startswith(''.''): str = str[1:len(str)] return str module_names = all_test_modules(''.'', ''*Tests.py'') suites = [unittest.defaultTestLoader.loadTestsFromName(mname) for mname in module_names] testSuite = unittest.TestSuite(suites) runner = unittest.TextTestRunner(verbosity=1) runner.run(testSuite)

El código busca en todos los subdirectorios de . para *Tests.py archivos que luego se cargan. Espera que cada *Tests.py contenga una sola clase *Tests(unittest.TestCase) que se cargan y se ejecutan una tras otra.

Esto funciona con la anidación profunda y arbitraria de directorios / módulos, pero cada directorio intermedio debe contener al menos un archivo __init__.py vacío. Esto permite que la prueba cargue los módulos anidados al reemplazar las barras inclinadas (o barras invertidas) por puntos (vea replace_slash_by_dot ).


Bueno, al estudiar el código de arriba un poco (específicamente usando TextTestRunner y TextTestRunner ), pude acercarme bastante. Finalmente, solucioné mi código al pasar todas las suites de prueba a un solo constructor de suites, en lugar de agregarlas "manualmente", lo que solucionó mis otros problemas. Así que aquí está mi solución.

import glob import unittest test_files = glob.glob(''test_*.py'') module_strings = [test_file[0:len(test_file)-3] for test_file in test_files] suites = [unittest.defaultTestLoader.loadTestsFromName(test_file) for test_file in module_strings] test_suite = unittest.TestSuite(suites) test_runner = unittest.TextTestRunner().run(test_suite)

Sí, probablemente es más fácil usar solo la nariz que hacer esto, pero eso no es lo importante.


Con Python 2.7 y versiones posteriores, no tiene que escribir código nuevo ni usar herramientas de terceros para hacer esto; La ejecución de la prueba recursiva a través de la línea de comando está incorporada.

python -m unittest discover <test_directory> # or python -m unittest discover -s <directory> -p ''*_test.py''

Puede leer más en la documentación de la prueba de la unidad python 2.7 o python 3.x.



En Python 3, si está usando unittest.TestCase y tiene un archivo __init__.py vacío (o de otro modo) en su directorio de prueba, puede ejecutar todas las pruebas con

python -m unittest

¡Hecho! Una solución de menos de 100 líneas. Esperemos que otro principiante de python ahorre tiempo al encontrar esto.


En el caso de una biblioteca o aplicación empaquetada , no desea hacerlo. setuptools lo hará por ti .

Para usar este comando, las pruebas de su proyecto deben envolverse en un conjunto de pruebas de prueba de unidad mediante una función, una clase o método TestCase o un módulo o paquete que TestCase clases TestCase . Si la suite nombrada es un módulo, y el módulo tiene una función additional_tests() , se llama y el resultado (que debe ser una prueba de unittest.TestSuite . unittest.TestSuite ) se agrega a las pruebas que se ejecutarán. Si la suite nombrada es un paquete, todos los submódulos y subpaquetes se agregan recursivamente a la suite de prueba general .

Solo dile dónde está tu paquete de prueba de raíz, como:

setup( # ... test_suite = ''somepkg.test'' )

Y ejecute python setup.py test .

El descubrimiento basado en archivos puede ser problemático en Python 3, a menos que evite las importaciones relativas en su conjunto de pruebas, porque el discover utiliza la importación de archivos. A pesar de que soporta top_level_dir opcional, pero tuve algunos errores de recursión infinitos. Por lo tanto, una solución simple para un código no empaquetado es colocar lo siguiente en __init__.py de su paquete de prueba (ver Protocolo load_tests ).

import unittest from . import foo, bar def load_tests(loader, tests, pattern): suite = unittest.TestSuite() suite.addTests(loader.loadTestsFromModule(foo)) suite.addTests(loader.loadTestsFromModule(bar)) return suite


Este es mi enfoque al crear un contenedor para ejecutar pruebas desde la línea de comandos:

#!/usr/bin/env python3 import os, sys, unittest, argparse, inspect, logging if __name__ == ''__main__'': # Parse arguments. parser = argparse.ArgumentParser(add_help=False) parser.add_argument("-?", "--help", action="help", help="show this help message and exit" ) parser.add_argument("-v", "--verbose", action="store_true", dest="verbose", help="increase output verbosity" ) parser.add_argument("-d", "--debug", action="store_true", dest="debug", help="show debug messages" ) parser.add_argument("-h", "--host", action="store", dest="host", help="Destination host" ) parser.add_argument("-b", "--browser", action="store", dest="browser", help="Browser driver.", choices=["Firefox", "Chrome", "IE", "Opera", "PhantomJS"] ) parser.add_argument("-r", "--reports-dir", action="store", dest="dir", help="Directory to save screenshots.", default="reports") parser.add_argument(''files'', nargs=''*'') args = parser.parse_args() # Load files from the arguments. for filename in args.files: exec(open(filename).read()) # See: http://codereview.stackexchange.com/q/88655/15346 def make_suite(tc_class): testloader = unittest.TestLoader() testnames = testloader.getTestCaseNames(tc_class) suite = unittest.TestSuite() for name in testnames: suite.addTest(tc_class(name, cargs=args)) return suite # Add all tests. alltests = unittest.TestSuite() for name, obj in inspect.getmembers(sys.modules[__name__]): if inspect.isclass(obj) and name.startswith("FooTest"): alltests.addTest(make_suite(obj)) # Set-up logger verbose = bool(os.environ.get(''VERBOSE'', args.verbose)) debug = bool(os.environ.get(''DEBUG'', args.debug)) if verbose or debug: logging.basicConfig( stream=sys.stdout ) root = logging.getLogger() root.setLevel(logging.INFO if verbose else logging.DEBUG) ch = logging.StreamHandler(sys.stdout) ch.setLevel(logging.INFO if verbose else logging.DEBUG) ch.setFormatter(logging.Formatter(''%(asctime)s %(levelname)s: %(name)s: %(message)s'')) root.addHandler(ch) else: logging.basicConfig(stream=sys.stderr) # Run tests. result = unittest.TextTestRunner(verbosity=2).run(alltests) sys.exit(not result.wasSuccessful())

Por motivos de simplicidad, disculpe mis estándares de codificación no PEP8 .

Luego, puede crear la clase BaseTest para componentes comunes para todas sus pruebas, de modo que cada una de sus pruebas simplemente se vería así:

from BaseTest import BaseTest class FooTestPagesBasic(BaseTest): def test_foo(self): driver = self.driver driver.get(self.base_url + "/")

Para ejecutar, simplemente especifique pruebas como parte de los argumentos de la línea de comando, por ejemplo:

./run_tests.py -h http://example.com/ tests/**/*.py


Este script BASH ejecutará el directorio de prueba de prueba de python desde CUALQUIER LUGAR en el sistema de archivos, sin importar en qué directorio de trabajo se encuentre: su directorio de trabajo siempre estará donde se encuentre ese directorio de test .

TODAS LAS PRUEBAS, $ PWD independientes

El módulo Python de unittest es sensible a su directorio actual, a menos que le diga dónde (usando la opción discover -s ).

Esto es útil cuando se mantiene en el directorio de trabajo ./src o ./example y necesita una prueba de unidad general rápida:

#!/bin/bash this_program="$0" dirname="`dirname $this_program`" readlink="`readlink -e $dirname`" python -m unittest discover -s "$readlink"/test -v

PRUEBAS SELECCIONADAS, $ PWD independientes

runone.py este archivo de utilidad: runone.py y lo uso así:

runone.py <test-python-filename-minus-dot-py-fileextension>

#!/bin/bash this_program="$0" dirname="`dirname $this_program`" readlink="`readlink -e $dirname`" (cd "$dirname"/test; python -m unittest $1)

No es necesario un archivo de test/__init__.py para cargar su paquete / sobrecarga de memoria durante la producción.


Esto ahora es posible directamente desde unittest: unittest.TestLoader.discover .

import unittest loader = unittest.TestLoader() start_dir = ''path/to/your/test/files'' suite = loader.discover(start_dir) runner = unittest.TextTestRunner() runner.run(suite)


He utilizado el método de discover y una sobrecarga de load_tests para lograr este resultado en un número de líneas de código (mínimo, creo):

def load_tests(loader, tests, pattern): '''''' Discover and load all unit tests in all files named ``*_test.py`` in ``./src/`` '''''' suite = TestSuite() for all_test_suite in unittest.defaultTestLoader.discover(''src'', pattern=''*_tests.py''): for test_suite in all_test_suite: suite.addTests(test_suite) return suite if __name__ == ''__main__'': unittest.main()

Ejecución en fives algo así como

Ran 27 tests in 0.187s OK


Intenté varios enfoques, pero todos parecen defectuosos o tengo que maquillar un código, eso es molesto. Pero hay una forma conveniente en Linux, que es simplemente encontrar cada prueba a través de cierto patrón y luego invocarlas una por una.

find . -name ''Test*py'' -exec python ''{}'' /;

y lo más importante, definitivamente funciona.


Podría usar un corredor de prueba que haría esto por usted. nose es muy buena por ejemplo. Cuando se ejecute, encontrará pruebas en el árbol actual y las ejecutará.

Actualizado:

Aquí hay un código de mis días anteriores a la nariz. Probablemente no desee la lista explícita de nombres de módulos, pero quizás el resto le sea útil.

testmodules = [ ''cogapp.test_makefiles'', ''cogapp.test_whiteutils'', ''cogapp.test_cogapp'', ] suite = unittest.TestSuite() for t in testmodules: try: # If the module defines a suite() function, call it to get the suite. mod = __import__(t, globals(), locals(), [''suite'']) suitefn = getattr(mod, ''suite'') suite.addTest(suitefn()) except (ImportError, AttributeError): # else, just load all the test cases from the module. suite.addTest(unittest.defaultTestLoader.loadTestsFromName(t)) unittest.TextTestRunner().run(suite)


Si desea ejecutar todas las pruebas de varias clases de casos de prueba y está feliz de especificarlas explícitamente, puede hacerlo de la siguiente manera:

from unittest import TestLoader, TextTestRunner, TestSuite from uclid.test.test_symbols import TestSymbols from uclid.test.test_patterns import TestPatterns if __name__ == "__main__": loader = TestLoader() tests = [ loader.loadTestsFromTestCase(test) for test in (TestSymbols, TestPatterns) ] suite = TestSuite(tests) runner = TextTestRunner(verbosity=2) runner.run(suite)

donde uclid es mi proyecto y TestSymbols y TestPatterns son subclases de TestCase .


Utilizo PyDev / LiClipse y realmente no he descubierto cómo ejecutar todas las pruebas a la vez desde la GUI. (Editar: hace clic derecho en la carpeta de prueba raíz y elige Run as -> Python unit-test

Esta es mi solución actual:

import unittest def load_tests(loader, tests, pattern): return loader.discover(''.'') if __name__ == ''__main__'': unittest.main()

Puse este código en un módulo llamado all en mi directorio de prueba. Si ejecuto este módulo como una prueba de unidad de LiClipse, se ejecutan todas las pruebas. Si le pido que repita solo pruebas específicas o fallidas, entonces solo se ejecutan esas pruebas. Tampoco interfiere con mi corredor de prueba de la línea de comandos (pruebas), se ignora.

Es posible que deba cambiar los argumentos para discover según la configuración de su proyecto.