Prueba unitaria de Python:¿Cómo ejecutar solo una parte de un archivo de prueba?
-
21-08-2019 - |
Pregunta
Tengo un archivo de prueba que contiene pruebas que toman bastante tiempo (envían cálculos a un clúster y esperan el resultado).Todos estos están en una clase TestCase específica.
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í), para poder ejecutar la mayoría de las pruebas con frecuencia y rapidez y todo el conjunto de vez en cuando, cuando tenga tiempo.
Por ahora solo uso unittest.main()
para iniciar las pruebas.
Gracias.
Solución
El valor predeterminado unittest.main()
usa el gestor de prueba predeterminado para hacer una TestSuite fuera del módulo en el que se está ejecutando principal.
Usted no tiene que utilizar este comportamiento por defecto.
Puede, por ejemplo, hacer tres casos unittest.TestSuite .
-
El subconjunto "rápida".
fast = TestSuite() fast.addTests( TestFastThis ) fast.addTests( TestFastThat )
-
El "lento" subconjunto.
slow = TestSuite() slow.addTests( TestSlowAnother ) slow.addTests( TestSlowSomeMore )
-
El "conjunto" establecido.
alltests = unittest.TestSuite([fast, slow])
Tenga en cuenta que he ajustado los nombres TestCase para indicar vs Rápido Lento. Usted puede subclase unittest.TestLoader para analizar los nombres de las clases y crear múltiples cargadores.
A continuación, el programa principal puede analizar los argumentos de línea de comandos con optparse o argparse (disponible desde 2.7 o 3.2) para recoger el cual privado que desee ejecutar, rápido, lento o todos.
O, puede confiar en que sys.argv[1]
es uno de los tres valores y el uso de algo tan simple como esto
if __name__ == "__main__":
suite = eval(sys.argv[1]) # Be careful with this line!
unittest.TextTestRunner().run(suite)
Otros consejos
Para ejecutar una única prueba específica que puede utilizar:
$ python -m unittest test_module.TestClass.test_method
Más información aquí
En realidad, se puede pasar los nombres del caso de prueba como sys.argv y se pondrá a prueba sólo aquellos casos.
Por ejemplo, suponga que tiene
class TestAccount(unittest.TestCase):
...
class TestCustomer(unittest.TestCase):
...
class TestShipping(unittest.TestCase):
...
account = TestAccount
customer = TestCustomer
shipping = TestShipping
Puede llamar
python test.py account
para tener pruebas de cuenta sólo, o incluso
$ python test.py account customer
tener ambos casos probados
Estoy haciendo esto mediante un sencillo 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 me falta decorar un caso de prueba ya existente con esta sola línea (sin necesidad de crear bancos de pruebas o similares, sólo que uno os.getenv()
línea de llamada en el comienzo de mi archivo de prueba de unidad), y como predeterminado esta prueba es ignorada.
Si quiero ejecutarlo a pesar de ser lento, acabo de llamar a mi script como el siguiente:
SLOW_TESTS=1 python -m unittest …
Hay básicamente dos maneras de hacerlo:
- Definir su propio conjunto de pruebas para la clase
- Crear clases simuladas de la conexión de clúster que devolverá los datos reales.
Soy un firme defensor de que el segundo enfoque; una prueba de unidad debe prueba sólo una unidad de código, y no los sistemas complejos (como bases de datos o clusters). Pero entiendo que no siempre es posible; A veces, la creación de maquetas es simplemente demasiado caro, o el objetivo de la prueba es realmente en el sistema complejo.
Volver a la opción (1), se puede proceder de esta manera:
suite = unittest.TestSuite()
suite.addTest(MyUnitTestClass('quickRunningTest'))
suite.addTest(MyUnitTestClass('otherTest'))
y luego pasando la suite con el corredor de prueba:
unittest.TextTestRunner().run(suite)
Más información en la documentación de Python: http://docs.python.org /library/unittest.html#testsuite-objects
Desde utiliza unittest.main()
que sólo puede funcionar 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, sólo tiene que hacer
python tests.py TestClass.test_method
O bien, puede hacer uso de la función de unittest.SkipTest()
. Ejemplo, agregue un método skipOrRunTest
a su clase de prueba como esta:
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!!!")
A continuación, agregue una llamada a este método skipOrRunTest al principio de cada una de las pruebas unitarias como esto:
def testType4(self):
self.skipOrRunTest('testType4')
Encontré otra solución, basada en cómo unittest.skip
trabajos de decorador.Al configurar el __unittest_skip__
y __unittest_skip_why__
.
Basado en etiquetas
Quería aplicar un sistema de etiquetado, etiquetar algunas pruebas como quick
, slow
, glacier
, memoryhog
, cpuhog
, core
, etcétera.
Entonces corre 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:
- Primero agregue etiquetas a las pruebas (a través de un personalizado
@testlabel
decorador de clase) - Costumbre
unittest.TestRunner
para identificar qué pruebas omitir y modificar el contenido de la lista de pruebas antes de ejecutar.
La implementación funcional se resume en esta esencia: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)
Todo MyTests1
Los exámenes de clase se omiten porque tiene la foo
etiqueta.
--whitelist
también funciona
Mira en el uso de un TestRunner dedicado, como py.test, la nariz o posiblemente incluso zope.testing. Todos ellos tienen opciones de línea de comandos para la selección de pruebas.
Mire por ejemplo, como la nariz: https://pypi.python.org/pypi /nose/1.3.0
He intentado @ 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 <module>
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
Los siguientes trabajó para mí:
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 la test_ * métodos que sólo desea ejecutar mediante la adición de un atributo a ellos. Es, básicamente, utiliza una metaclase para decorar las callables dentro de la clase TestCase que tienen el atributo StepDebug con un decorador unittest.skip. Más información sobre
Saltarse todas las pruebas unitarias pero uno en Python mediante el uso de decoradores y metaclases
No sé si es una solución mejor que los de arriba estoy limitarse a proporcionar como una opción.
No has encontrado una buena manera de hacer esto antes, lo que podrá compartir aquí.
Objetivo: Obtener un conjunto de archivos de prueba juntos para que puedan ser ejecutados como una unidad, pero todavía podemos seleccionar cualquiera de ellos para funcionar por sí mismo.
Problema: el método de descubrimiento no permiten una fácil selección de un solo caso de prueba para ejecutar
.Diseño: ver a continuación. Este aplana por lo que el espacio de nombres puede seleccionar el nombre de la clase TestCase, y dejar fuera de la la "tests1.test_core" prefijo:
./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 trabajó para mí.
if __name__ == '__main__':
unittest.main( argv=sys.argv, testRunner = unittest.TextTestRunner(verbosity=2))
Cuando me llamaron a pesar de que tenía que pasar el nombre del nombre de la clase y de prueba. Un pequeño inconveniente, ya que no tienen clase y nombre de la prueba combinación memorizado.
pitón ./tests.py class_Name.test_30311
La eliminación del nombre de la clase y el nombre de la prueba se ejecuta todas las pruebas en su archivo. Me parece mucho más fácil de tratar a continuación el construido en el método, ya que realmente no cambie de comando en la CLI. Sólo tiene que añadir el parámetro.
Disfrute, Keith
I creado un decorador que permite la corrección de las pruebas como pruebas lentas y para saltar ellos utilizando 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 puede marcar sus pruebas tan lento como esto:
@slow_test
def test_my_funky_thing():
perform_test()
Y omitir pruebas lentas estableciendo la variable de entorno SKIP_SLOW_TESTS
:
SKIP_SLOW_TESTS=1 python -m unittest