Unidades de prueba de interfaces en Python

Actualmente estoy aprendiendo python en preparación para una clase durante el verano y comencé implementando diferentes tipos de montones y estructuras de datos basadas en la prioridad.

Comencé a escribir un conjunto de pruebas unitarias para el proyecto, pero tuve dificultades para crear una prueba unitaria genérica que solo probara la interfaz y fuera ajena a la implementación real.

Me pregunto si es posible hacer algo como esto ..

suite = HeapTestSuite(BinaryHeap()) suite.run() suite = HeapTestSuite(BinomialHeap()) suite.run() 

Lo que estoy haciendo actualmente se siente … mal (herencia múltiple? ¡ACK!) ..

 class TestHeap: def reset_heap(self): self.heap = None def test_insert(self): self.reset_heap() #test that insert doesnt throw an exception... for x in self.inseq: self.heap.insert(x) def test_delete(self): #assert we get the first value we put in self.reset_heap() self.heap.insert(5) self.assertEquals(5, self.heap.delete_min()) #harder test. put in sequence in and check that it comes out right self.reset_heap() for x in self.inseq: self.heap.insert(x) for x in xrange(len(self.inseq)): val = self.heap.delete_min() self.assertEquals(val, x) class BinaryHeapTest(TestHeap, unittest.TestCase): def setUp(self): self.inseq = range(99, -1, -1) self.heap = BinaryHeap() def reset_heap(self): self.heap = BinaryHeap() class BinomialHeapTest(TestHeap, unittest.TestCase): def setUp(self): self.inseq = range(99, -1, -1) self.heap = BinomialHeap() def reset_heap(self): self.heap = BinomialHeap() if __name__ == '__main__': unittest.main() 

Personalmente me gusta más la generación de pruebas de nariz para este tipo de cosas. Entonces lo escribiría como:

 # They happen to all be simple callable factories, if they weren't you could put # a function in here: make_heaps = [BinaryHeap, BinomialHeap] def test_heaps(): for make_heap in make_heaps: for checker in checkers: # we'll set checkers later yield checker, make_heap def check_insert(make_heap): heap = make_heap() for x in range(99, -1, -1): heap.insert(x) # def check_delete_min etc. checkers = [ value for name, value in sorted(globals().items()) if name.startswith('check_')] 

¿Por qué no usar un alias para la clase que quieres probar? Puede escribir su clase de prueba refiriéndose a una clase HeapImpl falsa y luego asignarle una implementación específica antes de cada ejecución de prueba:

 class TestHeap(unittest.TestCase): def setUp(self): self.heap = HeapImpl() #test cases go here if __name__ == '__main__' suite = unittest.TestLoader().loadTestsFromTestCase(TestHeap) heaps = [BinaryHeap, BinomialHeap] for heap in heaps: HeapImpl = heap unittest.TextTestRunner().run(suite) 

Siempre y cuando cumplan con la interfaz que está utilizando en la suite de prueba, esto debería funcionar bien. Además, puede probar fácilmente tantas implementaciones como desee, solo agréguelas a la lista de heaps .

No creo que el patrón anterior sea terrible, pero la herencia múltiple ciertamente no es una idea.

Supongo que la razón por la que no puede ser que TestHeap sea una subclase de TestCase es porque se seleccionará automáticamente y se ejecutará como prueba, sin saber que debe ser subclasificado.

He solucionado este problema de otras dos maneras:

  1. En lugar de agregar funciones de prueba, tenga métodos de escritura que no se seleccionen automáticamente, luego agregue prueba () a cada una de sus subclases. Obviamente no es lo ideal.
  2. Reescribió unittest para no apestar, permitiendo la opción de establecer __test__ = False para la clase base. (Ver Testificar )