2009-07-10 17 views
12

Io uso NMock2, e ho redatto le seguenti classi NMock per rappresentare alcuni comuni concetti quadro finto:Quando aspettarsi e quando fermarsi?

  • Expect: questa specifica quale un metodo deriso deve restituire e dice che la chiamata deve avvenire o il test fallisce (se accompagnato da una chiamata allo VerifyAllExpectationsHaveBeenMet()).

  • Stub: specifica cosa deve restituire un metodo di simulazione ma non può causare il fallimento di un test.

Quindi, cosa dovrei fare quando?

+0

L'attesa restituirà nulla mentre Stub restituirà ciò che viene specificato. entrambi "stub" il metodo una volta – zinking

risposta

15

Un sacco di scherno quadri stanno portando i concetti di mock & stub più stretti & più vicini al punto che essi possono essere considerati funzionalmente quasi la stessa. Dal punto di vista concettuale però, io di solito cerco di seguire questa convenzione:

  • Mock: Solo quando si sta esplicitamente cercando di verificare il comportamento dell'oggetto in prova (cioè il test sta dicendo che questo oggetto deve chiamare quell'oggetto).
  • Stub: Quando si tenta di testare alcune funzionalità/comportamento, ma per farlo funzionare è necessario fare affidamento su alcuni oggetti esterni (ad esempio il test sta dicendo che questo oggetto deve fare qualcosa, ma come un lato effetto, essa può avvalersi quell'oggetto)

Ciò diventa più chiaro quando vi assicurate che ognuno dei vostri test di unità unica prova una cosa. Certo, se provi a testare tutto in un test, tanto vale aspettarti di tutto. Ma aspettando solo le cose che il test di unità specifico sta cercando, il tuo codice è molto più chiaro perché puoi vedere a colpo d'occhio qual è lo scopo del test.

Un altro vantaggio di questo è che sarete leggermente più isolati dal cambiamento & ottenere migliori messaggi di errore quando una modifica provoca un'interruzione. In altre parole, se si sottrae una parte della propria implementazione, è più probabile che si verifichi una rottura del test case, che vi mostrerà esattamente ciò che è rotto, piuttosto che un'intera serie di test che rompono & creando solo rumore.

Edit: Potrebbe essere più chiaro sulla base di un esempio forzato in cui gli audit un oggetto calcolatrice tutte le aggiunte ad un database (in pseudo-codice) ...

public void CalculateShouldAddTwoNumbersCorrectly() { 
    var auditDB = //Get mock object of Audit DB 
    //Stub out the audit functionality... 
    var calculator = new Calculator(auditDB); 
    int result = calculator.Add(1, 2); 
    //assert that result is 3 
} 

public void CalculateShouldAuditAddsToTheDatabase() { 
    var auditDB = //Get mock object of Audit DB 
    //Expect the audit functionality... 
    var calculator = new Calculator(auditDB); 
    int result = calculator.Add(1, 2); 
    //verify that the audit was performed. 
} 

Quindi nel primo caso test che abbiamo 'testando la funzionalità del metodo Add & non importa se un evento di verifica si verifica o meno, ma sappiamo che la calcolatrice non funzionerà senza un riferimento di auditDB, quindi lo stubmo solo per darci il minimo di funzionalità per far funzionare il nostro caso di test specifico.Nel secondo test stiamo testando specificamente che quando si fa un Add, si verifica l'evento di controllo, quindi qui usiamo le aspettative (notiamo che non ci interessa nemmeno quale sia il risultato, dal momento che non è quello che stiamo testando).

Sì, è possibile combinare i due casi in uno, & fare aspettative e affermare che il risultato è 3, ma poi si stanno testando due casi in un test di unità. Ciò renderebbe i test più fragili (poiché c'è una superficie più ampia di cose che potrebbero cambiare per interrompere il test) e meno chiaro (poiché quando il test combinato fallisce non è immediatamente evidente quale sia il problema .. l'aggiunta non funziona, o l'audit non funziona?)

+0

Non sono sicuro di come sia con altri framework di simulazione, ma con NMock, se inizi a controllare un Expectation, sei al corrente di * tutte * le chiamate esterne, il che a volte crea un onere schiacciante. Mi rendo conto che questo requisito ci spinge verso una migliore progettazione, ma in molti casi, voglio scrivere test unitari per codice legacy in cui non posso permettermi di eseguire molto refactoring nel tempo che devo prima di spedire. –

+2

Hmm ... questo fa la differenza. Ho usato principalmente RhinoMocks, che ti permette di Aspettarsi un metodo e poi Stub altri. I miei punti rimangono per lo più validi (vedi il mio esempio aggiunto), ma nel tuo caso, suppongo che dovrai eseguire tutti i test basati sulle tue aspettative (ad esempio test comportamentali) in un test. – Alconja

+0

Ack, avrei dovuto chiarire (e potrei voler menzionare questo nel corpo della mia domanda): puoi anche combinare Expects e Stub in NMock. Il problema è che spesso mi piacerebbe solo specificare una Expectation che non ha nemmeno bisogno di stubare il resto (ancora una volta, questo è particolarmente vero quando ho a che fare con codice che potrebbe usare una buona solida riprogettazione - quando ci sono numerosi hidden livelli di chiamate esterne). Oh, e +1. –

1

Bene ... IMHO non può essere più semplice: se il tuo test riguarda l'assicurazione che il tuo Presenter chiamerà Salva, fai un Aspetto. se il tuo test riguarda l'assicurazione che il tuo Presenter gestirà l'eccezione con garbo se Salva gira, fai uno Stub.

Per ulteriori dettagli, visitate this podcast by Hanselman and Osherove (autore di The Art Of Unit Testing)

+0

+1 per un buon caso d'uso, anche se è molto stretto - penso che la domanda potrebbe essere ancora * abbastanza * un po 'più semplice. –

+0

OK. Se insisti, ti darò da mangiare da solo :) L'unit test dovrebbe testare solo una cosa. Mai due. Dovrebbe o verificare se SUT (System Under Test) delega correttamente alcune azioni a una delle sue dipendenze. O dovrebbe verificare se SUT reagisce correttamente a un risultato restituito da una dipendenza. Se prima, usa Mock. Se secondo, usa Stub. In caso di dubbio, usa Jack of Diamonds. – zvolkov

4

"Aspettatevi azioni, stub queries". Se la chiamata dovrebbe cambiare lo stato del mondo al di fuori dell'oggetto sottoposto a test, allora rendila un'aspettativa - ti interessa come viene chiamato. Se si tratta solo di una query, è possibile chiamarla una o sei volte senza modificare lo stato del sistema, quindi interrompere la chiamata.

Un'ultima cosa, si noti che la distinzione è tra matrici e aspettative, cioè chiamate individuali, non necessariamente interi oggetti.

Problemi correlati