2009-11-13 23 views
18

Sto cercando di migliorare il numero e la qualità dei test nei miei progetti Python. Una delle difficoltà che ho riscontrato quando il numero di test aumenta è sapere cosa fa ogni test e come dovrebbe aiutare a individuare i problemi. So che la parte di tenere traccia dei test è meglio i nomi dei test di unità (che sono stati indirizzati a elsewhere), ma sono anche interessato a capire come documentazione e unit test vanno insieme.Come devono essere documentati i test unitari?

In che modo è possibile documentare i test delle unità per migliorare la loro utilità quando tali test falliscono in futuro? In particolare, cosa rende un buon test di unità docstring?

Apprezzerei sia le risposte descrittive che gli esempi di unit test con un'eccellente documentazione. Sebbene io stia lavorando esclusivamente con Python, sono aperto a pratiche di altre lingue.

risposta

13

mi documento più sul mio test di unità con il nome del metodo esclusivamente:

testInitializeSetsUpChessBoardCorrectly() 
testSuccessfulPromotionAddsCorrectPiece() 

Per quasi il 100% dei miei casi di test, questo spiega chiaramente qual è il test di unità sta convalidando e questo è tutto che uso. Tuttavia, in alcuni dei casi di test più complicati, aggiungerò alcuni commenti in tutto il metodo per spiegare cosa stanno facendo diverse linee.

Ho visto uno strumento prima (credo fosse per Ruby) che generava file di documentazione analizzando i nomi di tutti i casi di test in un progetto, ma non ricordo il nome. Se avete avuto casi di test per uno scacchi classe regina:

testCanMoveStraightUpWhenNotBlocked() 
testCanMoveStraightLeftWhenNotBlocked() 

lo strumento sarebbe generare un documento HTML con contenuto qualcosa di simile:

Queen requirements: 
- can move straight up when not blocked. 
- can move straight left when not blocked. 
+0

Che succede con i nomi delle tue funzioni?Suppongo che tu chiami i tuoi test "testFunctionName" e va bene, ma hai seriamente una funzione chiamata InitializeSetsUpChessBoardCorrectly? Penso che "setUpChessboard" farebbe bene. –

+10

No, il nome del metodo spiega esattamente cosa sta testando - quel test case verifica che initalize() imposta correttamente la scacchiera. Boom, documentazione automatica. –

+0

Haha sì, il "test" all'inizio è solo dai vecchi tempi di JUnit, su cui il mio cervello è ancora bloccato. Potrei chiamarlo initalizeSetsUpChessBoardCorrectly() e usare un'annotazione @Test. –

4

Il nome del metodo di prova deve descrivere esattamente ciò che sei test. La documentazione dovrebbe dire cosa fa fallire il test.

1

È necessario utilizzare una combinazione di nomi di metodi descrittivi e commenti nella stringa doc. Un buon modo per farlo è includere una procedura di base e passaggi di verifica nella stringa doc. Quindi, se si eseguono questi test da una sorta di framework di test che automatizza l'esecuzione dei test e la raccolta dei risultati, è possibile fare in modo che il log di struttura contenga il contenuto della stringa doc per ogni metodo di prova insieme al suo stdout + stderr.

Ecco un esempio di base:

class SimpelTestCase(unittest.TestCase): 
    def testSomething(self): 
     """ Procedure: 
      1. Print something 
      2. Print something else 
      --------- 
      Verification: 
      3. Verify no errors occurred 
     """ 
     print "something" 
     print "something else" 

Avere la procedura con il test rende molto più facile per capire ciò che il test sta facendo. E se si include la docstring con l'output del test, è possibile capire cosa è andato storto quando si è passati attraverso i risultati in seguito, molto più facilmente. Il posto precedente in cui ho lavorato ha fatto qualcosa di simile e ha funzionato molto bene quando si sono verificati degli errori. Abbiamo eseguito i test unitari su ogni check-in automatico, usando CruiseControl.

+0

http://blog.codinghorror.com/code-tells-you-how-comments-tell-you-why/ –

8

Forse il problema non è il modo migliore per scrivere le didascalie del test, ma come scrivere i test stessi? Eseguire i test di refactoring in modo tale che la loro autodocumentazione possa fare molta strada e la docstring non si esaurirà quando il codice cambia.

Ci sono alcune cose che puoi fare per rendere i test più chiaro:

  • chiare & nomi dei metodi di test descrittivi (già citato)
  • corpo di prova dovrebbe essere chiaro e conciso (auto documentazione)
  • astratto complicato setup/teardown ecc. nei metodi
  • altro?

Ad esempio, se si dispone di un test come questo:

def test_widget_run_returns_0(): 
    widget = Widget(param1, param2, "another param") 
    widget.set_option(true) 
    widget.set_temp_dir("/tmp/widget_tmp") 
    widget.destination_ip = "10.10.10.99" 

    return_value = widget.run() 

    assert return_value == 0 
    assert widget.response == "My expected response" 
    assert widget.errors == None 

Si potrebbe sostituire le dichiarazioni di impostazione con una chiamata di metodo:

def test_widget_run_returns_0(): 
    widget = create_basic_widget() 
    return_value = widget.run() 
    assert return_value == 0 
    assert_basic_widget(widget) 

def create_basic_widget(): 
    widget = Widget(param1, param2, "another param") 
    widget.set_option(true) 
    widget.set_temp_dir("/tmp/widget_tmp") 
    widget.destination_ip = "10.10.10.99" 
    return widget 

def assert_basic_widget(): 
    assert widget.response == "My expected response" 
    assert widget.errors == None 

Nota che il metodo di prova è ora composto di una serie di chiamate di metodo con nomi che rivelano l'intenzione, una sorta di DSL specifico per i tuoi test. Un test del genere ha ancora bisogno di documentazione?

Un'altra cosa da notare è che il metodo di test è principalmente a un livello di astrazione. Qualcuno leggendo il metodo di prova vedrà l'algoritmo è:

  • la creazione di un widget
  • chiamando corsa sul widget
  • affermando il codice ha fatto quello che ci aspettiamo

La loro comprensione del metodo di prova non è confuso dai dettagli della configurazione del widget, che è un livello di astrazione inferiore al metodo di test.

La prima versione del metodo di prova segue il modello Inline Setup. La seconda versione segue i modelli Creation Method e Delegated Setup.

Generalmente sono contrario ai commenti, tranne dove spiegano il "perché" del codice. Leggere lo zio Bob Martin Clean Code mi ha convinto di questo. C'è un capitolo sui commenti, e c'è un capitolo sui test. Lo consiglio.

Per ulteriori informazioni sulle best practice per i test automatici, consultare xUnit Patterns.

+0

Grazie per il risorse aggiuntive e aiutandomi a capire come semplificare i test stessi. Farò sicuramente qualche altra lettura sull'argomento. Ancora una volta, grazie! – ddbeck

0

Quando il test non riesce (che dovrebbe essere prima che passi), dovresti vedere il messaggio di errore ed essere in grado di dire cosa succede. Succede solo se lo pianifichi in quel modo.

Si tratta interamente della denominazione della classe di test, del metodo di test e del messaggio di asserzione. Quando un test fallisce, e non puoi dire cosa succede da questi tre indizi, rinominare alcune cose o suddividere alcune classi di test.

Non succede se il nome del dispositivo è ClassXTests e il nome del test è TestMethodX e il messaggio di errore è "previsto vero, restituito falso". Questo è un segno di sciatto test di scrittura.

Nella maggior parte dei casi non è necessario leggere il test o eventuali commenti per sapere cosa è successo.

Problemi correlati