with tutorial framework espaƱol djangoproject desde con cero applications python unit-testing testcase

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:

  1. Ninguno de los métodos en TestCase usa super() por lo que tendría que listar primero su clase para que métodos como setUp() y tearDown() funcionen.
  2. pylint advertirá que la clase base usa self.assertEquals() etc. que no están definidas en self 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)