2010-08-30 16 views
11

Dopo aver letto un interessante articolo sul comportamento dell'unità di test invece che sullo stato, mi sono reso conto che i miei test unitari spesso sono strettamente accoppiati al mio codice perché sto usando i mock. Non riesco a immaginare i test delle unità di scrittura senza fare il mock ma il fatto è che questi mock stanno accoppiando il mio test unitario molto al mio codice a causa delle chiamate expect andReturn.Sto facendo qualcosa di fondamentalmente sbagliato nei miei test unitari?

Ad esempio, quando creo un test che utilizza una simulazione, registro tutte le chiamate al mock specifico e assegna i valori di ritorno. Ora, quando cambio l'implementazione del codice effettivo per qualsiasi motivo, molti test si interrompono perché quella telefonata non era prevista dalla simulazione, costringendomi ad aggiornare anche il test dell'unità e mi costringeva a implementare ogni modifica due volte. Questo succede molto.

Questo problema è intrinseco all'uso di mock e dovrei imparare a conviverci o sto facendo qualcosa di fondamentalmente sbagliato? Per favore chiariscimi :) Gli esempi chiari che arrivano con la spiegazione sono i benvenuti, ovviamente.

+2

Si dovrebbe definitivamente google "classicista mockist fowler", prenotare un giorno o due e leggere :). imho, entrambi hanno i loro meriti ed entrambi dovrebbero essere usati quando la situazione lo richiede. –

risposta

5

quando creo un test che utilizza un modello, mi registrare tutte le chiamate alla specifica finto e assegnare valori di ritorno

Suona come si può essere troppo specifica aspettative.

Provare a compilare il minor numero di setup possibile nei test: stub (anziché prevedere) tutto il comportamento che non riguarda il test corrente e specifica solo i valori di ritorno assolutamente necessari per far funzionare il test.

This answer include un esempio conciso (oltre a una spiegazione alternativa più dettagliata).

2

Se si stanno riparando i test perché si rompono, non li si utilizza come previsto.

Se il comportamento di un metodo cambia, nello sviluppo basato su test si dovrebbe prima modificare il test per aspettarsi il nuovo comportamento, quindi implementare il nuovo comportamento.

+0

Corretto, e una definizione molto concisa di TDD, ma non cambia il fatto che devi sempre implementare le modifiche due volte, prima nel test, vederlo fallire, quindi nel codice, vederlo avere successo. Quindi questo 'implementa sempre il doppio problema' intrinseco al TDD? – nkr1pt

+0

@ nkr1pt: Sì, ma non è isolato da TDD. Avresti più o meno lo stesso problema in qualsiasi modello di sviluppo che incorpori test in qualsiasi forma. È solo che è così ovvio in TDD. – Guffa

4

La mia esperienza è quella di utilizzare i mock solo ai limiti dei (sotto) sistemi. Se ho due classi fortemente correlate, non li deretto appartati ma li collaudo insieme. Un esempio potrebbe essere un composito e un visitatore. Se provo un visitatore concreto, non uso una simulazione per il composito, ma creo dei veri compositi. Si potrebbe obiettare che questo non è un test unitario (dipende dalla definizione di cosa è un'unità). Ma non importa così tanto. Quello che cerco di ottenere è:

  1. Scrittura test leggibili (i test senza i mock sono spesso più facili da leggere).
  2. Testare solo un'area di codice focalizzata (nell'esempio il visitatore concreato e la parte rilevante del composito).
  3. Scrivere test rapidi (a patto che crei solo poche classi, nell'esempio i compositi in calcestruzzo, questa non è una preoccupazione ... guarda le creazioni transitive).

Solo se incontro il limite di un sottosistema, utilizzo i mock. Esempio: ho un composito che può renderizzarsi su un renderer. Mi piacerebbe prendere in giro il renderer se eseguo il test della logica di rendering del composito.

Il comportamento di test anziché lo stato sembra inizialmente promettente, ma in generale testerei lo stato poiché i test risultanti sono facili da mantenere. I mazzi sono un cannone. Non rompere un dado con una mazza.

+1

Mentre sono d'accordo con il senso di ciò, molti sistemi sono abbastanza superficiali in un numero sufficiente di punti che c'è una minima differenza tra il beffardo tutto il tempo e il mocking solo ai confini del sottosistema. –

+0

In questo caso: dai qualche profondità ai sistemi. Separare chiaramente i sottosistemi! – Arne

+0

Se ti senti in grado di testare un certo numero di classi insieme come unità, forse il prossimo passo per te è consolidare quell'unità in una singola classe. – Ladlestein

0

Molte buone risposte già qui, ma per me una buona regola è quella di testare i requisiti del metodo, non l'implementazione. A volte ciò può significare utilizzare un oggetto fittizio perché l'interazione è il requisito, ma di solito è meglio testare il valore di ritorno del metodo o il cambiamento di stato dell'oggetto.

Problemi correlati