unit - Prueba de unidad Python con base y clase secundaria
unittest python 3 (12)
Actualmente tengo algunas pruebas unitarias que comparten un conjunto común de pruebas. Aquí hay un ejemplo:
import unittest
class BaseTest(unittest.TestCase):
def testCommon(self):
print ''Calling BaseTest:testCommon''
value = 5
self.assertEquals(value, 5)
class SubTest1(BaseTest):
def testSub1(self):
print ''Calling SubTest1:testSub1''
sub = 3
self.assertEquals(sub, 3)
class SubTest2(BaseTest):
def testSub2(self):
print ''Calling SubTest2:testSub2''
sub = 4
self.assertEquals(sub, 4)
if __name__ == ''__main__'':
unittest.main()
El resultado de lo anterior es:
Calling BaseTest:testCommon
.Calling BaseTest:testCommon
.Calling SubTest1:testSub1
.Calling BaseTest:testCommon
.Calling SubTest2:testSub2
.
----------------------------------------------------------------------
Ran 5 tests in 0.000s
OK
¿Hay alguna manera de volver a escribir lo anterior para que no se testCommon
primer testCommon
?
EDITAR: en lugar de ejecutar 5 pruebas arriba, quiero que ejecute solo 4 pruebas, 2 de SubTest1 y otras 2 de SubTest2. Parece que Python unittest está ejecutando el BaseTest original por sí mismo y necesito un mecanismo para evitar que eso suceda.
¿Qué estás intentando lograr? Si tiene un código de prueba común (aserciones, pruebas de plantilla, etc.), colóquelos en métodos que no tengan un prefijo de test
por lo que unittest
no los cargará.
import unittest
class CommonTests(unittest.TestCase):
def common_assertion(self, foo, bar, baz):
# whatever common code
self.assertEqual(foo(bar), baz)
class BaseTest(CommonTests):
def testCommon(self):
print ''Calling BaseTest:testCommon''
value = 5
self.assertEquals(value, 5)
class SubTest1(CommonTests):
def testSub1(self):
print ''Calling SubTest1:testSub1''
sub = 3
self.assertEquals(sub, 3)
class SubTest2(CommonTests):
def testSub2(self):
print ''Calling SubTest2:testSub2''
sub = 4
self.assertEquals(sub, 4)
if __name__ == ''__main__'':
unittest.main()
Así que este es un hilo viejo, pero me encontré con este problema hoy y pensé en mi propio truco. Utiliza un decorador que realiza los valores de las funciones Ninguno cuando se accede a través de la clase base. No necesita preocuparse por la configuración y la clase de configuración porque si la clase base no tiene pruebas, no se ejecutarán.
import types
import unittest
class FunctionValueOverride(object):
def __init__(self, cls, default, override=None):
self.cls = cls
self.default = default
self.override = override
def __get__(self, obj, klass):
if klass == self.cls:
return self.override
else:
if obj:
return types.MethodType(self.default, obj)
else:
return self.default
def fixture(cls):
for t in vars(cls):
if not callable(getattr(cls, t)) or t[:4] != "test":
continue
setattr(cls, t, FunctionValueOverride(cls, getattr(cls, t)))
return cls
@fixture
class BaseTest(unittest.TestCase):
def testCommon(self):
print(''Calling BaseTest:testCommon'')
value = 5
self.assertEqual(value, 5)
class SubTest1(BaseTest):
def testSub1(self):
print(''Calling SubTest1:testSub1'')
sub = 3
self.assertEqual(sub, 3)
class SubTest2(BaseTest):
def testSub2(self):
print(''Calling SubTest2:testSub2'')
sub = 4
self.assertEqual(sub, 4)
if __name__ == ''__main__'':
unittest.main()
Cambie el nombre del método BaseTest para setUp:
class BaseTest(unittest.TestCase):
def setUp(self):
print ''Calling BaseTest:testCommon''
value = 5
self.assertEquals(value, 5)
class SubTest1(BaseTest):
def testSub1(self):
print ''Calling SubTest1:testSub1''
sub = 3
self.assertEquals(sub, 3)
class SubTest2(BaseTest):
def testSub2(self):
print ''Calling SubTest2:testSub2''
sub = 4
self.assertEquals(sub, 4)
Salida:
Ran 2 pruebas en 0.000s
Llamar a BaseTest: testCommon Calling
SubTest1: testSub1 Llamando
Prueba base: testCommon Calling
SubTest2: testSub2
De la documentation :
TestCase.setUp ()
Método llamado para preparar el dispositivo de prueba. Esto se llama inmediatamente antes de llamar al método de prueba; cualquier excepción planteada por este método se considerará un error en lugar de una falla de prueba. La implementación predeterminada no hace nada.
Hice casi lo mismo que @Vladim P. ( https://.com/a/25695512/2451329 ) pero ligeramente modificado:
import unittest2
from some_module import func1, func2
def make_base_class(func):
class Base(unittest2.TestCase):
def test_common1(self):
print("in test_common1")
self.assertTrue(func())
def test_common2(self):
print("in test_common1")
self.assertFalse(func(42))
return Base
class A(make_base_class(func1)):
pass
class B(make_base_class(func2)):
def test_func2_with_no_arg_return_bar(self):
self.assertEqual("bar", func2())
y ahí vamos
La respuesta de Matthew Marshall es excelente, pero requiere que heredes de dos clases en cada uno de tus casos de prueba, que es propensa a errores. En cambio, uso esto (python> = 2.7):
class BaseTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
if cls is BaseTest:
raise unittest.SkipTest("Skip BaseTest tests, it''s a base class")
super(BaseTest, cls).setUpClass()
La respuesta de Matthew es la que necesitaba usar ya que estoy en 2.5 todavía. Pero a partir de 2.7 puede usar el decorador @ unittest.skip () en cualquier método de prueba que quiera omitir.
http://docs.python.org/library/unittest.html#skipping-tests-and-expected-failures
Tendrá que implementar su propio decorador de saltos para verificar el tipo de base. No he usado esta característica antes, pero en la parte superior de mi cabeza puedes usar BaseTest como un tipo de marcador para condicionar el salto:
def skipBaseTest(obj):
if type(obj) is BaseTest:
return unittest.skip("BaseTest tests skipped")
return lambda func: func
No uses herencia múltiple, te morderá más later .
En su lugar, puede mover su clase base al módulo separado o envolverlo con la clase en blanco:
import unittest
class BaseTestCases:
class BaseTest(unittest.TestCase):
def testCommon(self):
print ''Calling BaseTest:testCommon''
value = 5
self.assertEquals(value, 5)
class SubTest1(BaseTestCases.BaseTest):
def testSub1(self):
print ''Calling SubTest1:testSub1''
sub = 3
self.assertEquals(sub, 3)
class SubTest2(BaseTestCases.BaseTest):
def testSub2(self):
print ''Calling SubTest2:testSub2''
sub = 4
self.assertEquals(sub, 4)
if __name__ == ''__main__'':
unittest.main()
La salida:
Calling BaseTest:testCommon
.Calling SubTest1:testSub1
.Calling BaseTest:testCommon
.Calling SubTest2:testSub2
.
----------------------------------------------------------------------
Ran 4 tests in 0.001s
OK
Otra opción es no ejecutar
unittest.main()
En lugar de eso puedes usar
suite = unittest.TestLoader().loadTestsFromTestCase(TestClass)
unittest.TextTestRunner(verbosity=2).run(suite)
Entonces solo ejecutas las pruebas en la clase TestClass
Puede resolver este problema con un solo comando:
del(BaseTest)
Entonces el código se vería así:
import unittest
class BaseTest(unittest.TestCase):
def testCommon(self):
print ''Calling BaseTest:testCommon''
value = 5
self.assertEquals(value, 5)
class SubTest1(BaseTest):
def testSub1(self):
print ''Calling SubTest1:testSub1''
sub = 3
self.assertEquals(sub, 3)
class SubTest2(BaseTest):
def testSub2(self):
print ''Calling SubTest2:testSub2''
sub = 4
self.assertEquals(sub, 4)
del(BaseTest)
if __name__ == ''__main__'':
unittest.main()
Simplemente cambie el nombre del método testCommon a otra cosa. Unittest (por lo general) se salta todo lo que no tenga ''test'' en él.
Rápido y simple
import unittest
class BaseTest(unittest.TestCase):
def methodCommon(self):
print ''Calling BaseTest:testCommon''
value = 5
self.assertEquals(value, 5)
class SubTest1(BaseTest):
def testSub1(self):
print ''Calling SubTest1:testSub1''
sub = 3
self.assertEquals(sub, 3)
class SubTest2(BaseTest):
def testSub2(self):
print ''Calling SubTest2:testSub2''
sub = 4
self.assertEquals(sub, 4)
if __name__ == ''__main__'':
unittest.main()`
Una forma en que he pensado en resolver esto es ocultando los métodos de prueba si se usa la clase base. De esta forma, las pruebas no se saltan, por lo que los resultados de las pruebas pueden ser de color verde en lugar de amarillo en muchas herramientas de informes de pruebas.
Comparado con el método Mixin, ide como PyCharm no se quejará de que faltan métodos de prueba de la clase base.
Si una clase base hereda de esta clase, deberá anular los métodos setUpClass
y tearDownClass
.
class BaseTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls._test_methods = []
if cls is BaseTest:
for name in dir(cls):
if name.startswith(''test'') and callable(getattr(cls, name)):
cls._test_methods.append((name, getattr(cls, name)))
setattr(cls, name, lambda self: None)
@classmethod
def tearDownClass(cls):
if cls is BaseTest:
for name, method in cls._test_methods:
setattr(cls, name, method)
cls._test_methods = []
Usa la herencia múltiple, por lo que tu clase con pruebas comunes no hereda de TestCase.
import unittest
class CommonTests(object):
def testCommon(self):
print ''Calling BaseTest:testCommon''
value = 5
self.assertEquals(value, 5)
class SubTest1(unittest.TestCase, CommonTests):
def testSub1(self):
print ''Calling SubTest1:testSub1''
sub = 3
self.assertEquals(sub, 3)
class SubTest2(unittest.TestCase, CommonTests):
def testSub2(self):
print ''Calling SubTest2:testSub2''
sub = 4
self.assertEquals(sub, 4)
if __name__ == ''__main__'':
unittest.main()