Pregunta

Tengo un directorio que contiene mi Python unidad de pruebas.Cada unidad de módulo de prueba es de la forma test_*.py.Estoy tratando de hacer un archivo llamado all_test.py que va, lo has adivinado, ejecutar todos los archivos en el mencionado formulario de prueba y devolver el resultado.He probado dos métodos hasta ahora;ambos han fallado.Voy a mostrar dos de los métodos, y espero que hay alguien que sabe cómo hacer esto correctamente.

Para mi primer valiente intento, yo pensé: "Si me acaba de importar todos mis pruebas de los módulos en el archivo y, a continuación, llamar a este unittest.main() adorno, que va a trabajar, ¿verdad?" Bueno, resulta que yo 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()

Este trabajo no lo hizo, el resultado que obtuve fue:

$ python all_test.py 

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

Para mi segundo intento, pensé, ok, tal vez voy a probar a hacer esta prueba completo, cosa de una manera más "manual" de la moda.Así que traté de hacerlo 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 también no trabajo, pero me parece tan de 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

Me parece que tienen una suite de algún tipo, y puedo ejecutar el resultado.Estoy un poco preocupado por el hecho de que se dice que solo tengo run=1, parece que debería ser run=2, pero es el progreso.Pero, ¿cómo puedo pasar y mostrar el resultado en la principal?O ¿cómo puedo conseguir básicamente es trabajar así que sólo puedo ejecutar este archivo, y al hacerlo, ejecutar todos los tests de la unidad en este directorio?

¿Fue útil?

Solución

Se puede usar un corredor de prueba que había de hacer esto para usted. es muy bueno, por ejemplo. Cuando se ejecuta, encontrará pruebas en el árbol actual y ejecutarlos.

Actualizado:

Aquí hay un código de mis días antes de la nariz. Es probable que no desea que la lista explícita de nombres de los módulos, pero tal vez el resto va a ser útil para usted.

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)

Otros consejos

Con Python 2.7 y superior no tiene que escribir nuevo código o utilizar herramientas de terceros para hacer esto; la ejecución de pruebas recursiva a través de la línea de comandos es incorporado.

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

Puede leer más en el Python 2.7 o pitón 3.x unittest documentación.

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

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

runner = unittest.TextTestRunner()
runner.run(suite)

En Python 3, si usted está utilizando unittest.TestCase:

  • Debe tener un archivo __init__.py vacía (o no) en su directorio test ( debe ser nombrado test/)
  • Sus archivos de prueba dentro test/ coinciden con el patrón de test_*.py. Pueden estar dentro de un subdirectorio bajo test/, y los subdirectorios puede ser nombrado como nada.

A continuación, puede ejecutar todas las pruebas con:

python -m unittest

Listo! Una solución menos de 100 líneas. Con suerte otra principiante pitón ahorra tiempo al encontrar esto.

Bueno, estudiando el código de arriba un poco (en concreto utilizando TextTestRunner y defaultTestLoader), yo era capaz de llegar muy cerca. Con el tiempo me fijo mi código por el que pasa también acaba de todas las pruebas a un solo suites constructor, en lugar de la adición de ellos "manualmente", que fija 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í, es probable que sea más fácil usar la nariz que no hacer esto, pero eso no viene al caso.

Si desea ejecutar todas las pruebas de varias clases de casos de prueba y está contenta de especificar explícitamente a continuación, puede hacerlo de esta 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.

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

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()

La ejecución de algo así como cinco

Ran 27 tests in 0.187s
OK

He intentado diversos enfoques, pero todos parecen defectuoso o tengo que algún código de maquillaje, eso es molesto. Pero hay una manera conveniente bajo Linux, que es simplemente encontrar todas las pruebas a través de cierto patrón y luego invocar a uno por uno.

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

y lo más importante, definitivamente las obras.

En el caso de un empaquetado de la biblioteca o de la aplicación, usted no quiere hacerlo. setuptools lo hacemos por usted.

Para utilizar este comando, el proyecto de pruebas debe ser envuelto en una unittest suite de prueba por una función, una Prueba de la clase o método, o un módulo o paquete que contiene TestCase clases.Si el nombre de la suite es un módulo y el módulo tiene una additional_tests() función, se llama y el resultado (que debe ser una unittest.TestSuite se añade a la ejecución de las pruebas.Si el nombre de la suite es un paquete, cualquier submódulos y subpaquetes son recursivamente añadido a la general de la prueba suite.

Sólo digo que donde su raíz paquete de prueba es, como:

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

Y ejecutar python setup.py test.

Archivo basado en el descubrimiento podría ser problemático en Python 3, a menos que usted evite relativa de las importaciones en el conjunto de pruebas, porque discover utiliza el archivo de importación.Aunque admite opcional top_level_dir, pero he tenido algunos recursividad infinita de errores.Así que una solución simple para un no-código empaquetado es poner el siguiente en __init__.py de su paquete de prueba (ver load_tests Protocolo).

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

Yo uso PyDev / LiClipse y realmente no he encontrado la manera de ejecutar todas las pruebas a la vez desde la interfaz gráfica de usuario. (Edit: se hace clic derecho en la carpeta prueba de raíz y elegir 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()

Pongo este código en un módulo llamado all en mi directorio de prueba. Si me quedo este módulo como unittest de LiClipse continuación, se ejecutan todas las pruebas. Si sólo te pido que repetir pruebas específicas o fallidas y sólo aquellas pruebas se ejecutan. No interfiere con mi corredor de prueba de línea de comandos (ya sea nosetests) -. Se ignora

Es posible que necesite cambiar los argumentos a discover basado en la configuración de su proyecto.

Sobre la base de la respuesta de Stephen Cagle añadí 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 archivos para . *Tests.py que se cargan a continuación. Se espera que cada *Tests.py para contener un único *Tests(unittest.TestCase) clase que se carga a su vez y ejecutado uno tras otro.

Esto funciona con profundidad de anidación arbitraria de directorios / módulos, pero cada directorio en el medio debe contener un archivo __init__.py vacío por lo menos. Esto permite que la prueba para cargar los módulos anidados mediante la sustitución de barras (o barras invertidas) por puntos (ver replace_slash_by_dot).

Debido a que el descubrimiento de prueba parece ser un tema completo, hay algún marco dedicado a probar descubrimiento:

Más leyendo aquí: https://wiki.python.org/moin/PythonTestingToolsTaxonomy

Este script bash ejecutará la pitón unittest directorio de prueba desde cualquier parte del sistema de archivos, no importa lo que el directorio de trabajo se encuentra en:. Su directorio de trabajo esté siempre donde se encuentra ese directorio test

todas las pruebas, independiente $ PWD

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

Esto es útil cuando se aloje en el directorio de trabajo o ./src ./example y que necesita una prueba general de la unidad 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, independiente $ PWD

nombro este archivo de la utilidad: runone.py y utilizar de esta manera:

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 hay necesidad de un archivo test/__init__.py a la carga de su paquete / memoria de sobrecarga durante la producción.

Aquí está mi enfoque mediante la creación de un envoltorio para funcionar pruebas de 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())

En aras de la simplicidad, disculpen mis estándares no PEP8 codificación .

A continuación, puede crear la clase BaseTest de componentes comunes para todas las pruebas, por lo que cada uno de su prueba sólo tendría que quedar así:

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

Para ejecutar, sólo tiene que especificar pruebas como parte de los argumentos de línea de comandos, por ejemplo:.

./run_tests.py -h http://example.com/ tests/**/*.py
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top