2010-05-26 17 views
6

Attualmente sto imparando Python in preperation per una classe durante l'estate e sono stato avviato implementando diversi tipi di heap e strutture di dati basate su priorità.Unit Testing Interfaces in Python

Ho iniziato a scrivere una suite di test unitari per il progetto, ma ho incontrato difficoltà nella creazione di un test unitario generico che verifica solo l'interfaccia ed è ignaro dell'attuale implementazione.

Mi chiedo se è possibile fare qualcosa di simile ..

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

Quello che sto facendo attualmente si sente proprio ... sbagliato (l'ereditarietà multipla? 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() 

risposta

4

Personalmente mi piace nose test generation altro per questo genere di cose. Quindi lo scriverei come:

# 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_')] 
1

Non penso che lo schema sopra sia terribile, ma l'ereditarietà multipla non è certamente un'idea.

Immagino che il motivo per cui non si può semplicemente avere TestHeap come sottoclasse di TestCase è perché verrà automaticamente rilevato ed eseguito come test, non sapendo che deve essere sottoposto a sottoclassi.

ho ottenuto intorno a questo problema altri due modi:

  1. Invece di aggiungere test_ funzioni, hanno metodi di scrittura che non automaticamente ottenere raccolti, quindi aggiungere di test() per ciascuna delle vostre sottoclassi. Ovviamente non è l'ideale.
  2. Rewrote unittest to not suck, che consente di impostare __test__ = False nella classe base. (Vedi Testify)
1

Perché non utilizzare solo un alias per la classe che si desidera testare? È possibile scrivere la classe di test riferendosi a un falso HeapImpl di classe, e quindi assegnare una specifica implementazione ad esso prima di ogni prova:

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) 

Finché sono conformi con l'interfaccia che si sta utilizzando nella suite di test, questo dovrebbe funzionare bene Inoltre, puoi facilmente testare tutte le implementazioni che desideri, basta aggiungerle all'elenco heaps.