python - framework - tutorial django
caso de prueba abstracto usando python unittest (7)
Hay una manera muy simple que todos se han perdido hasta ahora. Y a diferencia de varias de las respuestas, funciona con todos los controladores de prueba, en lugar de fallar en el momento en que cambia de uno a otro.
Simplemente use la herencia como de costumbre, luego agregue:
del AbstractTestCase
al final del módulo.
¿Es posible crear un TestCase
abstracto, que tendrá algunos métodos test_ *, pero este TestCase
no se llamará y esos métodos solo se usarán en subclases? Creo que tendré un TestCase
abstracto en mi suite de pruebas y se subclasificará para una implementación diferente de una interfaz única. Esta es la razón por la cual todos los métodos de prueba son algunos, solo uno, cambios en el método interno. ¿Cómo puedo hacerlo de manera elegante?
La biblioteca de prueba de Python tiene el protocolo load_tests , que se puede usar para lograr exactamente lo que quieres:
# Add this function to module with AbstractTestCase class
def load_tests(loader, tests, _):
result = []
for test_case in tests:
if type(test_case._tests[0]) is AbstractTestCase:
continue
result.append(test_case)
return loader.suiteClass(result)
La herencia múltiple no es una gran opción aquí, principalmente por las dos siguientes razones:
- Ninguno de los métodos en
TestCase
usasuper()
por lo que tendría que listar primero su clase para que métodos comosetUp()
ytearDown()
funcionen. - pylint advertirá que la clase base usa
self.assertEquals()
etc. que no están definidas enself
en ese punto.
Este es el truco con el que se me ocurrió: convertir run()
en un no-op para la clase base solamente.
class TestBase( unittest.TestCase ):
def __init__( self, *args, **kwargs ):
super( TestBase, self ).__init__( *args, **kwargs )
self.helper = None
# Kludge alert: We want this class to carry test cases without being run
# by the unit test framework, so the `run'' method is overridden to do
# nothing. But in order for sub-classes to be able to do something when
# run is invoked, the constructor will rebind `run'' from TestCase.
if self.__class__ != TestBase:
# Rebind `run'' from the parent class.
self.run = unittest.TestCase.run.__get__( self, self.__class__ )
else:
self.run = lambda self, *args, **kwargs: None
def newHelper( self ):
raise NotImplementedError()
def setUp( self ):
print "shared for all subclasses"
self.helper = self.newHelper()
def testFoo( self ):
print "shared for all subclasses"
# test something with self.helper
class Test1( TestBase ):
def newHelper( self ):
return HelperObject1()
class Test2( TestBase ):
def newHelper( self ):
return HelperObject2()
No entendí muy bien qué es lo que planeas hacer, la regla de oro es "no ser inteligente con las pruebas", solo tienes que escribirlas allí.
Pero para lograr lo que desea, si hereda de unittest.TestCase, cada vez que llame a unittest.main () se ejecutará su clase "abstracta"; creo que esta es la situación que desea evitar.
Solo haz esto: crea tu clase "abstracta" heredando de "objeto", no de TestCase. Y para las implementaciones "concretas", simplemente use herencia múltiple: herede de unittest.TestCase y de su clase abstracta.
import unittest
class Abstract(object):
def test_a(self):
print "Running for class", self.__class__
class Test(Abstract, unittest.TestCase):
pass
unittest.main()
actualización : invierte el orden de herencia - TestCase
primero para que sus definiciones no sean TestCase
por los TestCase
predeterminados de TestCase
, como se señala en los comentarios a continuación.
Si realmente desea usar herencia en lugar de mixins, una solución simple es anidar la prueba abstracta en otra clase.
Evita problemas con el descubrimiento de corredores de prueba y aún puede importar la prueba abstracta desde otro módulo.
import unittest
class AbstractTests(object):
class AbstractTest(unittest.TestCase)
def test_a(self):
print "Running for class", self.__class__
class Test(AbstractTests.AbstractTest):
pass
Si sigues la convención de listar explícitamente todas las clases de prueba en run_unittest (mira, por ejemplo, el conjunto de pruebas de Python para muchos usos de esa convención), entonces será sencillo no incluir una clase específica.
Si desea continuar usando unittest.main, y si puede permitir el uso de unittest2 (por ejemplo, de Python 2.7), puede usar su protocolo load_tests para especificar qué clases contienen casos de prueba). En versiones anteriores, deberá subclasificar TestLoader y anular loadTestsFromModule .
Solo para poner mi granito de arena, aunque es probable que vaya en contra de alguna convención, podría definir su caso de prueba abstracta como un miembro protegido para evitar su ejecución. Implementé lo siguiente en Django y funciona según las necesidades. Vea el ejemplo a continuación.
from django.test import TestCase
class _AbstractTestCase(TestCase):
"""
Abstract test case - should not be instantiated by the test runner.
"""
def test_1(self):
raise NotImplementedError()
def test_2(self):
raise NotImplementedError()
class TestCase1(_AbstractTestCase):
"""
This test case will pass and fail.
"""
def test_1(self):
self.assertEqual(1 + 1, 2)
class TestCase2(_AbstractTestCase):
"""
This test case will pass successfully.
"""
def test_1(self):
self.assertEqual(2 + 2, 4)
def test_2(self):
self.assertEqual(12 * 12, 144)