Python unittest: ¿cómo ejecutar solo parte de un archivo de prueba?

Tengo un archivo de prueba que contiene pruebas que llevan bastante tiempo (envían los cálculos a un grupo y esperan el resultado). Todos estos están en la clase específica de TestCase.

Dado que toman tiempo y, además, no es probable que se rompan, me gustaría poder elegir si este subconjunto de pruebas se ejecuta o no (la mejor manera sería con un argumento de línea de comandos, es decir, ” ./tests.py --offline “o algo así, así podría ejecutar la mayoría de las pruebas a menudo y rápidamente y todo el conjunto de vez en cuando, cuando tengo tiempo.

Por ahora, solo uso unittest.main() para iniciar las pruebas.

Gracias.

El unittest.main() predeterminado usa el cargador de prueba predeterminado para hacer un TestSuite fuera del módulo en el que se está ejecutando el principal.

No tienes que usar este comportamiento predeterminado.

Puede, por ejemplo, hacer tres instancias de unittest.TestSuite .

  1. El subconjunto “rápido”.

     fast = TestSuite() fast.addTests( TestFastThis ) fast.addTests( TestFastThat ) 
  2. El subconjunto “lento”.

     slow = TestSuite() slow.addTests( TestSlowAnother ) slow.addTests( TestSlowSomeMore ) 
  3. El conjunto “entero”.

     alltests = unittest.TestSuite([fast, slow]) 

Tenga en cuenta que he ajustado los nombres de TestCase para indicar rápido frente a lento. Puede subclase unittest.TestLoader para analizar los nombres de las clases y crear múltiples cargadores.

Luego, su progtwig principal puede analizar los argumentos de la línea de comandos con optparse o argparse (disponible desde 2.7 o 3.2) para elegir qué suite desea ejecutar, rápido, lento o todo.

O bien, puede confiar en que sys.argv[1] es uno de los tres valores y usar algo tan simple como este

 if __name__ == "__main__": suite = eval(sys.argv[1]) # Be careful with this line! unittest.TextTestRunner().run(suite) 

Para ejecutar solo una prueba específica puede usar:

 $ python -m unittest test_module.TestClass.test_method 

Mas informacion aqui

En realidad, uno puede pasar los nombres del caso de prueba como sys.argv y solo esos casos serán probados.

Por ejemplo, supongamos que usted tiene

 class TestAccount(unittest.TestCase): ... class TestCustomer(unittest.TestCase): ... class TestShipping(unittest.TestCase): ... account = TestAccount customer = TestCustomer shipping = TestShipping 

Puedes llamar

 python test.py account 

tener solo pruebas de cuenta, o incluso

 $ python test.py account customer 

tener ambos casos probados

Tienes básicamente dos formas de hacerlo:

  1. Define tu propio conjunto de pruebas para la clase.
  2. Cree clases simuladas de la conexión de clúster que devolverán los datos reales.

Soy un firme defensor del segundo enfoque; una prueba de unidad debe probar solo una unidad de código, y no sistemas complejos (como bases de datos o clusters). Pero entiendo que no siempre es posible; a veces, crear simulaciones es simplemente demasiado costoso, o el objective de la prueba es realmente en el sistema complejo.

De vuelta a la opción (1), puede proceder de esta manera:

 suite = unittest.TestSuite() suite.addTest(MyUnitTestClass('quickRunningTest')) suite.addTest(MyUnitTestClass('otherTest')) 

y luego pasar la suite al corredor de prueba:

 unittest.TextTestRunner().run(suite) 

Más información sobre la documentación de python: http://docs.python.org/library/unittest.html#testsuite-objects

Estoy haciendo esto usando un simple skipIf :

 import os SLOW_TESTS = int(os.getenv('SLOW_TESTS', '0')) @unittest.skipIf(not SLOW_TESTS, "slow") class CheckMyFeature(unittest.TestCase): def runTest(self): … 

De esta manera, solo necesito decorar un caso de prueba ya existente con esta línea única (no es necesario crear suites de prueba o similar, solo esa línea de llamada os.getenv() al principio de mi archivo de prueba de la unidad), y por defecto esto prueba se salta.

Si quiero ejecutarlo a pesar de ser lento, solo llamo a mi script así:

 SLOW_TESTS=1 python -m unittest … 

Ya que usa unittest.main() , solo puede ejecutar python tests.py --help para obtener la documentación:

 Usage: tests.py [options] [test] [...] Options: -h, --help Show this message -v, --verbose Verbose output -q, --quiet Minimal output -f, --failfast Stop on first failure -c, --catch Catch control-C and display results -b, --buffer Buffer stdout and stderr during test runs Examples: tests.py - run default set of tests tests.py MyTestSuite - run suite 'MyTestSuite' tests.py MyTestCase.testSomething - run MyTestCase.testSomething tests.py MyTestCase - run all 'test*' test methods in MyTestCase 

Es decir, simplemente puedes hacer

 python tests.py TestClass.test_method 

O puede hacer uso de la función unittest.SkipTest() . Ejemplo, agregue un método skipOrRunTest a su clase de prueba como este:

 def skipOrRunTest(self,testType): #testsToRun = 'ALL' #testsToRun = 'testType1, testType2, testType3, testType4,...etc' #testsToRun = 'testType1' #testsToRun = 'testType2' #testsToRun = 'testType3' testsToRun = 'testType4' if ((testsToRun == 'ALL') or (testType in testsToRun)): return True else: print "SKIPPED TEST because:\n\t testSuite '" + testType + "' NOT IN testsToRun['" + testsToRun + "']" self.skipTest("skipppy!!!") 

Luego, agregue una llamada a este método skipOrRunTest al comienzo de cada una de las pruebas unitarias de esta manera:

 def testType4(self): self.skipOrRunTest('testType4') 

Encontré otra solución, basada en cómo funciona el decorador unittest.skip . Al configurar __unittest_skip__ y __unittest_skip_why__ .

Basado en tags

Quería aplicar un sistema de etiquetado, etiquetar algunas pruebas como quick , slow , glacier , memoryhog , cpuhog , core , etc.

Luego ejecute all 'quick' tests , o run everything except 'memoryhog' tests , su configuración básica de lista blanca / lista negra

Implementación

Implementé esto en 2 partes:

  1. Primero agregue tags a las pruebas (a través de un decorador de clase @testlabel personalizado)
  2. unittest.TestRunner personalizado para identificar qué pruebas omitir y modificar el contenido de la lista de pruebas antes de ejecutar.

La implementación de trabajo está en esta lista: https://gist.github.com/fragmuffin/a245f59bdcd457936c3b51aa2ebb3f6c

(un ejemplo completamente funcional fue demasiado largo para ponerlo aquí)

El resultado es …

 $ ./runtests.py --blacklist foo test_foo (test_things.MyTest2) ... ok test_bar (test_things.MyTest3) ... ok test_one (test_things.MyTests1) ... skipped 'label exclusion' test_two (test_things.MyTests1) ... skipped 'label exclusion' ---------------------------------------------------------------------- Ran 4 tests in 0.000s OK (skipped=2) 

Todas las pruebas de clase MyTests1 se omiten porque tiene la etiqueta foo .

--whitelist también funciona

Busque el uso de un probador dedicado, como py.test, nose o posiblemente zope.testing. Todos ellos tienen opciones de línea de comando para seleccionar pruebas.

Busque por ejemplo como Nariz: https://pypi.python.org/pypi/nose/1.3.0

Intenté la respuesta de @ slott:

 if __name__ == "__main__": suite = eval(sys.argv[1]) # Be careful with this line! unittest.TextTestRunner().run(suite) 

Pero eso me dio el siguiente error:

 Traceback (most recent call last): File "functional_tests.py", line 178, in  unittest.TextTestRunner().run(suite) File "/usr/lib/python2.7/unittest/runner.py", line 151, in run test(result) File "/usr/lib/python2.7/unittest/case.py", line 188, in __init__ testMethod = getattr(self, methodName) TypeError: getattr(): attribute name must be string 

Lo siguiente me funcionó:

 if __name__ == "__main__": test_class = eval(sys.argv[1]) suite = unittest.TestLoader().loadTestsFromTestCase(test_class) unittest.TextTestRunner().run(suite) 

He encontrado otra forma de seleccionar los métodos test_ * que solo quiero ejecutar agregándoles un atributo. Básicamente, utiliza una metaclase para decorar los callables dentro de la clase TestCase que tienen el atributo StepDebug con un decorador unittest.skip. Más información sobre

Se omiten todas las pruebas de unidad, pero una en Python mediante el uso de decoradores y metaclases

No sé si es una solución mejor que las anteriores, solo la ofrezco como una opción.

No he encontrado una buena manera de hacer esto antes, así que compartir aquí.

Objetivo: reunir un conjunto de archivos de prueba para que puedan ejecutarse como una unidad, pero aún podemos seleccionar cualquiera de ellos para que se ejecute solo.

Problema: el método de descubrimiento no permite la fácil selección de un solo caso de prueba para ejecutar.

Diseño: ver abajo. Esto aplana el espacio de nombres, por lo que se puede seleccionar por nombre de clase TestCase y omitir el prefijo “tests1.test_core”:

 ./run-tests TestCore.test_fmap 

Código

  test_module_names = [ 'tests1.test_core', 'tests2.test_other', 'tests3.test_foo', ] loader = unittest.defaultTestLoader if args: alltests = unittest.TestSuite() for a in args: for m in test_module_names: try: alltests.addTest( loader.loadTestsFromName( m+'.'+a ) ) except AttributeError as e: continue else: alltests = loader.loadTestsFromNames( test_module_names ) runner = unittest.TextTestRunner( verbosity = opt.verbose ) runner.run( alltests ) 

Esto es lo único que me funcionó.

 if __name__ == '__main__': unittest.main( argv=sys.argv, testRunner = unittest.TextTestRunner(verbosity=2)) 

Cuando lo llamé, tuve que pasar el nombre de la clase y el nombre de la prueba. Un pequeño inconveniente ya que no tengo memorizada la combinación de clase y nombre de prueba.

python ./tests.py class_Name.test_30311

Al eliminar el nombre de la clase y el nombre de la prueba se ejecutan todas las pruebas en su archivo. Me parece que es MUCHO más fácil de manejar que el método incorporado ya que realmente no cambio mi comando en la CLI. Solo agrega el parámetro.

Disfruta, keith

Creé un decorador que permite marcar pruebas como pruebas lentas y omitirlas usando una variable de entorno

 from unittest import skip import os def slow_test(func): return skipIf('SKIP_SLOW_TESTS' in os.environ, 'Skipping slow test')(func) 

Ahora puedes marcar tus exámenes como lentos así:

 @slow_test def test_my_funky_thing(): perform_test() 

Y omita las pruebas lentas configurando la variable de entorno SKIP_SLOW_TESTS :

 SKIP_SLOW_TESTS=1 python -m unittest