Se il tuo codice di test dell'unità "odora" è davvero importante?


52

Di solito lancio semplicemente i miei test unitari usando copia e incolla e tutti i tipi di altre cattive pratiche. I test unitari di solito sembrano piuttosto brutti, sono pieni di "odore di codice", ma importa davvero? Mi dico sempre finché il codice "reale" è "buono", è tutto ciò che conta. Inoltre, i test unitari di solito richiedono vari "hack puzzolenti" come le funzioni di stub.

Quanto dovrei preoccuparmi di test unitari mal "progettati" (maleodoranti)?


8
Quanto può essere difficile sistemare il codice che copi sempre?
JeffO,

7
l'odore di codice nei test potrebbe facilmente essere un indicatore di odore nascosto nel resto del codice - perché avvicinarsi ai test come se non fossero un codice "reale"?
HorusKol,

Risposte:


75

Gli odori dei test unitari sono importanti? Sì, sicuramente. Tuttavia, sono diversi dagli odori di codice perché i test unitari hanno uno scopo diverso e hanno un diverso set di tensioni che ne informano il design. Molti odori nel codice non si applicano ai test. Data la mia mentalità TDD, direi che gli odori di test unitari sono più importanti degli odori di codice perché il codice è proprio lì per soddisfare i test.

Ecco alcuni odori comuni di test unitari:

  • Fragilità : i tuoi test falliscono spesso e inaspettatamente anche per modifiche del codice apparentemente banali o non correlate?
  • Perdita di stato : i test falliscono in modo diverso a seconda, ad esempio, di quale ordine vengono eseguiti?
  • Setup / Teardown Bloat : i blocchi di setup / teardown sono lunghi e stanno diventando più lunghi? Eseguono qualche tipo di logica aziendale?
  • Runtime lento : l' esecuzione dei test richiede molto tempo? Qualcuno dei tuoi test unitari richiede più di un decimo di secondo per essere eseguito? (Sì, sono serio, un decimo di secondo.)
  • Attrito : i test esistenti rendono difficile scrivere nuovi test? Ti ritrovi a lottare con i fallimenti dei test spesso durante il refactoring?

L'importanza degli odori è che sono utili indicatori del design o di altre questioni più fondamentali, ovvero "dove c'è fumo, c'è fuoco". Non limitarti a cercare odori di prova, cerca anche la causa sottostante.

Qui, d'altra parte, ci sono alcune buone pratiche per i test unitari:

  • Feedback rapido e mirato : i test dovrebbero isolare rapidamente l'errore e fornire informazioni utili sulla sua causa.
  • Riduci al minimo la distanza del codice di prova : dovrebbe esserci un percorso chiaro e breve tra il test e il codice che lo implementa. Le lunghe distanze creano cicli di feedback inutilmente lunghi.
  • Prova una cosa alla volta : i test unitari dovrebbero testare solo una cosa. Se devi provare un'altra cosa, scrivi un'altra prova.
  • Un bug è un test che hai dimenticato di scrivere : cosa puoi imparare da questo fallimento nel scrivere test migliori e più completi in futuro?

2
"I test unitari hanno uno scopo diverso e hanno una diversa serie di tensioni che ne informano il design". Ad esempio, dovrebbero DAMP, non necessariamente SECCHI.
Jörg W Mittag,

3
@ Jörg Concordato, ma DAMP rappresenta davvero qualcosa? : D
Rein Henrichs,

5
@Rein: frasi descrittive e significative. Inoltre, non completamente ASCIUTTO. Vedi codeshelter.wordpress.com/2011/04/07/…
Robert Harvey,

+1. Inoltre non conoscevo il significato di DAMP.
egarcia,

2
@ironcode Se non riesci a lavorare su un sistema in isolamento senza il timore di interromperne l'integrazione con altri sistemi, questo puzza di stretto accoppiamento con me. Questo è precisamente lo scopo di identificare gli odori di prova come una lunga autonomia: ti informano di problemi di progettazione come l'accoppiamento stretto. La risposta non dovrebbe essere "Oh, allora quell'odore di prova non è valido", dovrebbe essere "Cosa mi dice questo odore del mio design?" I test unitari che specificano l'interfaccia esterna del tuo sistema dovrebbero essere sufficienti per dirti se le tue modifiche interrompono o meno l'integrazione con i suoi consumatori.
Rein Henrichs,

67

Essere preoccupato. Scrivi unit test per dimostrare che il tuo codice si comporta come ti aspetti. Ti permettono di refactoring rapidamente con sicurezza. Se i test sono fragili, difficili da comprendere o difficili da mantenere, ignorerai i test falliti o li spegnerai man mano che la tua base di codice si evolve, annullando in primo luogo molti dei vantaggi della scrittura dei test.


17
+1 Nel momento in cui inizi a ignorare i test non riusciti non aggiungono alcun valore.

19

Ho appena finito di leggere The Art of Unit Testing un paio di giorni fa. L'autore raccomanda di dedicare la stessa attenzione ai test unitari quanto al codice di produzione.

Ho sperimentato in prima persona test scritti male, non realizzabili. Ne ho scritti alcuni dei miei. È praticamente garantito che se un test è un dolore nel culo da mantenere, non verrà mantenuto. Una volta che i test non sono sincronizzati con il codice in esame, sono diventati nidi di bugie e inganni. Il punto centrale dei test unitari è infondere fiducia nel fatto che non abbiamo infranto nulla (ovvero, creano fiducia). Se i test non sono affidabili, sono peggio che inutili.


4
+1 devi mantenere i test, proprio come il codice di produzione
Hamish Smith,

Un altro ottimo libro su questo argomento è il "modello di test xUnit - Codice di test di refatcoring" di Gerard Meszaros.
adrianboimvaser,

7

Finché il test unitario in realtà verifica il codice nella maggior parte dei casi. (Detto più di proposito dal momento che a volte è difficile trovare tutti i possibili risultati). Penso che il codice "puzzolente" sia la tua preferenza personale. Scrivo sempre il codice in un modo in cui posso leggerlo e comprenderlo in pochi secondi, piuttosto che scavare nella spazzatura e cercare di capire cosa sia cosa. Soprattutto quando torni ad esso dopo un periodo di tempo significativo.

In conclusione: i suoi test, supponiamo che siano facili. Non vuoi confonderti con il codice "puzzolente".


5
+1: non agitarsi sul codice di test dell'unità. Alcune delle domande più stupide ruotano intorno a rendere il codice unit test più "robusto" in modo che un cambio di programmazione non interrompa il test unitario. Stupidità. I test unitari sono progettati per essere fragili. Va bene se sono meno robusti del codice dell'applicazione progettato per il test.
S.Lott

1
@ S.Lott Sia d'accordo che in disaccordo, per motivi meglio spiegati nella mia risposta.
Rein Henrichs,

1
@Rein Henrichs: sono odori molto, molto più gravi di quelli descritti nella domanda. "copia e incolla" e "sembra piuttosto brutto" non suonano così male come gli odori che descrivi dove i test sono inaffidabili.
S.Lott

1
@ S. Lott esattamente, penso che se parleremo di "odori di test unitari" è fondamentale fare questa distinzione. Gli odori di codice nei test unitari spesso non lo sono. Sono scritti per scopi diversi e le tensioni che li rendono puzzolenti in un contesto sono molto diverse nell'altro.
Rein Henrichs,

2
@ S. Lott tra questo sembra un'opportunità perfetta per usare la funzionalità di chat molto trascurata :)
Rein Henrichs,

6

Decisamente. Alcune persone dicono "qualsiasi test è meglio di nessun test". Non sono assolutamente d'accordo: i test scritti in modo errato riducono i tempi di sviluppo e finisci per perdere giorni a riparare i test "rotti" perché non erano buoni test unitari in primo luogo. Per me al momento, le due cose su cui mi sto concentrando per rendere preziosi i miei test, piuttosto che un onere, sono:

manutenibilità

Dovresti testare il risultato ( cosa succede), non il metodo ( come succede). L'impostazione per il test deve essere il più possibile disaccoppiata dall'implementazione: impostare solo i risultati per le chiamate di servizio ecc. Che sono assolutamente necessari.

  • Utilizzare un framework beffardo per assicurarsi che i test non dipendano da nulla di esterno
  • Favorisci i mozziconi sulle beffe (se la tua struttura si distingue tra loro) ove possibile
  • Nessuna logica nei test! Ifs, switch, for-eaches, case, try-catches ecc. Sono tutti grandi no-no, in quanto possono introdurre bug nel codice di test stesso

leggibilità

Va bene consentire un po 'più di ripetizione nei test, cosa che normalmente non permetteresti nel tuo codice di produzione, se li rende più leggibili. Basta bilanciare questo con le cose di manutenibilità sopra. Sii esplicito in quello che sta facendo il test!

  • Cerca di mantenere uno stile "organizza, agisci, asserisci" per i tuoi test. Ciò separa la configurazione e le aspettative dello scenario, dall'azione eseguita e dal risultato dichiarato.
  • Mantenere un'asserzione logica per test (se il nome del test contiene "e", potrebbe essere necessario suddividerlo in più test)

In conclusione, dovresti essere molto preoccupato per i test "puzzolenti": possono finire solo per perdere tempo, senza valore.

Hai detto:

I test unitari di solito richiedono vari "hack puzzolenti" come le funzioni di stub.

Sembra che potresti sicuramente fare con la lettura di alcune tecniche di Unit Testing, come l'uso di un framework Mocking, per rendere la tua vita molto più facile. Consiglio vivamente caldamente The Art of Unit Testing , che copre quanto sopra e molto altro ancora. L'ho trovato illuminante dopo aver lottato a lungo con test scritti male male, non mantenibili, "maleodoranti". È uno dei migliori investimenti di tempo che ho fatto quest'anno!


Risposta eccellente. Ho solo un piccolo cavillo: molto più importante di "un'asserzione logica per test" è un'azione per test. Il punto non importa quanti passi ci vogliono per organizzare un test, dovrebbe esserci solo una "azione del codice di produzione sotto test". Se tale azione dovesse avere più di un effetto collaterale, potresti avere più asserzioni.
Disilluso,

5

Due domande per te:

  • Sei assolutamente sicuro di provare ciò che pensi di provare?
  • Se qualcun altro guarda il test unitario, saranno in grado di capire cosa dovrebbe fare il codice ?

Esistono modi per gestire attività ripetitive nei test delle unità che sono più comunemente codice di installazione e smontaggio. In sostanza, si dispone di un metodo di impostazione del test e di un metodo di smontaggio del test: tutti i framework di unit test supportano questo aspetto.

I test unitari dovrebbero essere piccoli e facilmente comprensibili. Se non lo sono e il test ha esito negativo, come risolverai il problema in un periodo di tempo ragionevolmente breve. Semplifica te stesso per un paio di mesi lungo la strada quando devi tornare al codice.


5

Quando la spazzatura inizia a puzzare, è tempo di eliminarla. Il codice di test deve essere pulito come il codice di produzione. Lo mostreresti a tua madre?


3

Oltre alle altre risposte qui.

Il codice di scarsa qualità nei test unitari non è limitato alla suite di test unitari.

Uno dei ruoli ricoperti da Unit Testing è Documentation.

La suite di unit test è uno dei luoghi in cui uno cerca di scoprire come si intende utilizzare un'API.

È improbabile che i chiamanti della tua API copino parti della suite di test di unità, portando a un codice di suite di test errato che infetta il codice live altrove.


3

Quasi non ho inviato la mia risposta perché mi ci è voluto un po 'di tempo per capire come affrontarlo come una domanda legittima.

Le ragioni per cui i programmatori si impegnano nelle "migliori pratiche" non sono per ragioni estetiche o perché vogliono un codice "perfetto". È perché consente loro di risparmiare tempo.

  • Risparmia tempo perché un buon codice è più facile da leggere e capire.
  • Fa risparmiare tempo perché quando è necessario trovare una correzione un bug è più facile e veloce da individuare.
  • Risparmia tempo perché quando vuoi estendere il codice è più facile e veloce farlo.

Quindi la domanda che mi stai ponendo (dal mio punto di vista) è: dovrei risparmiare tempo o scrivere codice che mi farà perdere tempo?

A questa domanda posso solo dire: quanto è importante il tuo tempo?

Cordiali saluti: stubing, beffardo e patch di scimmia hanno tutti usi legittimi. "Annusano" solo quando il loro uso non è appropriato.


2

Cerco specificamente di NON rendere i miei test unitari troppo robusti. Ho visto unit test che iniziano a fare errori nel nome di essere robusti. Ciò che si finisce con sono test che inghiottono gli insetti che stanno cercando di catturare. I test unitari devono anche fare alcune cose funky abbastanza spesso per far funzionare le cose. Pensa all'intera idea di accessori privati ​​che usano la riflessione ... se ne vedessi un sacco di quelli nel codice di produzione, sarei preoccupato 9 volte su 10. Penso che dovrebbe essere dedicato più tempo a pensare a ciò che viene testato , piuttosto che pulizia del codice. In ogni caso, i test dovranno essere cambiati abbastanza frequentemente se esegui importanti refactoring, quindi perché non hackerarli insieme, avere un po 'meno sentimenti di proprietà ed essere più motivati ​​a riscriverli o rielaborarli quando sarà il momento?


2

Se stai cercando una copertura pesante e di basso livello, trascorrerai tutto il tempo o più nel codice di test che nel codice del prodotto quando effettui delle modifiche gravi.

A seconda della complessità della configurazione del test, il codice potrebbe essere più complesso. (httpcontext.current vs. l'orrendo mostro di tentare di costruirne uno falso, per esempio)

A meno che il tuo prodotto non sia qualcosa in cui raramente apporti modifiche alle interfacce esistenti e i tuoi input a livello di unità sono molto semplici da configurare, sarei almeno preoccupato della comprensione dei test come del prodotto reale.


0

Gli odori di codice sono solo un problema nel codice che richiede di essere modificato o compreso in un momento successivo.

Quindi penso che dovresti correggere gli odori di codice quando devi andare "vicino" al test unit dato, ma non prima.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.