TDD: Lo sto facendo bene?


14

Sono un nuovo programmatore (sto imparando da circa un anno) e nel mio obiettivo di migliorarlo ho appreso di recente su TDD. Volevo prendere l'abitudine di usarlo poiché sembra molto utile. Volevo controllare e assicurarmi di usarlo correttamente.

Cosa sto facendo:

  1. Pensa a un nuovo metodo di cui ho bisogno.
  2. Crea un test per quel metodo.
  3. Test fallito.
  4. Scrivi metodo.
  5. Passare il test.
  6. Metodo del refattore.
  7. Ripetere.

Lo sto facendo per OGNI metodo che scrivo, ci sono alcuni con cui non dovrei preoccuparmi? Più tardi di solito penso a un modo per testare i miei metodi già esistenti in un modo o una situazione diversi. Devo fare questi nuovi test a cui penso, o poiché ogni metodo ha già un test tutto suo non dovrei preoccuparmi? Posso superare il test del mio codice, credo sia la mia principale preoccupazione nel chiedere questo.

MODIFICARE

Inoltre, questo era qualcosa che mi stavo chiedendo. Quando si fa qualcosa come fare una GUI, TDD sarebbe necessario in quella situazione? Personalmente, non riesco a pensare a come scrivere test per questo.


5
Lo stai già facendo molto meglio dei professionisti esperti che affermano di provare tutto (ma non lo fanno).
yannis,

Quello che descrivi non è lo Spirito del TDD.

1
Potresti voler esaminare ATDD o BDD.
dietbuddha,

Forse inizia più in alto: pensa a un nuovo modulo che ti serve.

Risposte:


16

Quello che stai descrivendo come flusso di lavoro non è secondo me lo Spirito del TDD.

La sinossi del libro di Kent Becks su Amazon dice:

Molto semplicemente, lo sviluppo guidato dai test ha lo scopo di eliminare la paura nello sviluppo delle applicazioni.Mentre un po 'di paura è salutare (spesso vista come una coscienza che dice ai programmatori di "stare attenti!"), L'autore crede che i sottoprodotti della paura includano programmatori incerti, scontrosi e non comunicativi che non sono in grado di assorbire le critiche costruttive. Quando i team di programmazione acquistano in TDD, vedono immediatamente risultati positivi. Eliminano la paura implicita nel loro lavoro e sono meglio attrezzati per affrontare le difficili sfide che devono affrontare. TDD elimina le caratteristiche provvisorie, insegna ai programmatori a comunicare e incoraggia i membri del team a cercare critiche Tuttavia, anche l'autore ammette che la scontrosità deve essere elaborata individualmente! In breve, la premessa alla base di TDD è che il codice dovrebbe essere continuamente testato e refactored.

TDD pratico

Test automatizzati formali, in particolare Unit Testing, ogni metodo di ogni classe è altrettanto un anti-pattern e non verifica nulla. C'è un equilibrio da avere. Stai scrivendo unit test per ogni setXXX/getXXXmetodo, sono anche metodi!

Inoltre i test possono aiutare a risparmiare tempo e denaro, ma non dimenticare che costano tempo e denaro per lo sviluppo e sono codici, quindi costano tempo e denaro per la manutenzione. Se si atrofizzano per mancanza di manutenzione, diventano una responsabilità più che un vantaggio.

Come tutto questo, c'è un equilibrio che non può essere definito da nessuno tranne te stesso. Qualsiasi dogma in entrambi i casi è probabilmente più sbagliato che corretto.

Una buona metrica è il codice che è fondamentale per la logica aziendale e soggetto a frequenti modifiche basate su requisiti mutevoli. Queste cose necessitano di test formali che sono automatizzati, il che sarebbe un grande ritorno sugli investimenti.

Sarà molto difficile trovare molti negozi professionali che funzionano in questo modo. Non ha semplicemente senso spendere soldi per testare cose che a tutti gli effetti pratici non cambieranno mai dopo che è stato preformato un semplice test del fumo. Scrivere test unitari automatizzati formali per i .getXXX/.setXXXmetodi è un ottimo esempio di questo, completa perdita di tempo.

Sono ormai passati due decenni da quando è stato sottolineato che i test del programma possono dimostrare in modo convincente la presenza di bug, ma non possono mai dimostrare la loro assenza. Dopo aver citato devotamente questa osservazione ben pubblicizzata, l'ingegnere del software ritorna all'ordine del giorno e continua a perfezionare le sue strategie di test, proprio come l'alchimista di un tempo, che ha continuato a perfezionare le sue purificazioni crisocosmiche.

- Edsger W. Djikstra . (Scritto nel 1988, quindi ora è più vicino ai 4,5 decenni.)

Vedi anche questa risposta .


1
Questo affronta praticamente ciò di cui ero preoccupato. Sentivo che non avrei dovuto testare tutti i metodi come ero, ma non ne ero sicuro. Sembra che potrei ancora aver bisogno di leggere qualcosa in più sul TDD.
cgasser,

@kevincline La maggior parte del tempo setXXX/getXXXnon è affatto necessaria :)
Chip

1
Quando memorizzi quel banale getXXX e lo sbagli, o introduci un caricamento pigro in getXXX e lo sbagli, allora saprai che a volte vuoi davvero testare i tuoi vincitori.
Frank Shearar,

13

Sei molto vicino. Prova a pensare in questo modo leggermente diverso.

  1. Pensa a un nuovo comportamento di cui ho bisogno.
  2. Crea un test per quel comportamento.
  3. Test fallito.
  4. Scrivi nuovo o estendi il metodo esistente.
  5. Passare il test.
  6. Codice refactor.
  7. Ripetere.

Non creare automaticamente getter e setter per ogni proprietà . Non pensare a un intero metodo e scrivi i test per coprire tutte le funzionalità . Prova a incapsulare le proprietà all'interno della classe e scrivi i metodi per fornire il comportamento necessario. Lascia che i tuoi metodi si evolvano in un buon design invece di cercare di pianificarli in anticipo. Ricorda che TDD è un processo di progettazione, non un processo di test. Il vantaggio che ha rispetto ad altri processi di progettazione sta lasciando dietro di sé un flusso di test di regressione automatizzati, piuttosto che un pezzo di carta che butti nel cestino.

Inoltre, ricorda le tre regole di TDD di zio Bob .

  1. Non è consentito scrivere alcun codice di produzione a meno che non si debba effettuare un test unit unit fallito.
  2. Non è consentito scrivere più unit test di quanto sia sufficiente per fallire; e gli errori di compilazione sono errori.
  3. Non è consentito scrivere più codice di produzione di quanto sia sufficiente per superare un test unitario non riuscito.

1
@Zexanima: Stai andando molto meglio di molti di noi dopo un anno. Sto solo cercando di indicarti il ​​passaggio successivo.
pdr,

2
Penso che queste 3 regole a cui ti colleghi; per quanto idilliaci possano sembrare, sono eccezionalmente dogmatici e altamente irrealisticamente rigidi nel 99% di tutti i negozi di produzione che chiunque incontrerà.

1
@FrankShearar o può essere visto come il blatering poco pratico di un estremista fondamentalista e ignorato all'ingrosso. Ho lavorato in negozi che avevano questo atteggiamento dogmatico, hanno preso il dogma alla lettera e hanno perso il punto; scrivere test che non mettessero alla prova nessuno dei loro codici reali in modo pratico e finendo semplicemente per testare la capacità dei framework di Iniezione di Derisione e Dipendenza di confondere ciò che era importante nella migliore delle ipotesi.

1
@pdr Lo Spirito di qualcosa è diametralmente opposto alla canonizzazione formalizzata dogmatica di quella cosa. Una cosa è avere una filosofia e un'altra trasformarla in una religione . TDD è più volte che non conversato in termini religiosi dogmatici in bianco e nero . Che 3 regole suonino dogmatiche e religiose nella presentazione e ciò che viene ascoltato è il mantra Test, Test, Test , a qualcuno come l'OP, le prendono alla lettera e ciò causa più danni che benefici. Ho contrastato Frank che affermazioni polarizzanti possono fare più male alla causa che bene.

2
Il mio punto era che il dogmatismo deriva dall'accettare ciecamente qualcosa come vangelo . Prendi la dichiarazione polarizzante, provala, ti costringe a uscire dalla tua zona di comfort. Non è possibile valutare i compromessi coinvolti nel TDD se non si prova l'approccio estremo a 3 punti tutto o niente, poiché non si avranno dati .
Frank Shearar,

5

Poche cose da aggiungere alle risposte degli altri:

  1. C'è qualcosa come test eccessivo. Vuoi assicurarti che i test unitari si sovrappongano il meno possibile. Non ha senso avere più test per verificare le stesse condizioni nello stesso codice. D'altra parte, quando si esegue il refactoring del codice di produzione e si hanno molti test che si sovrappongono a quella sezione, sarà necessario tornare indietro e correggere tutti quei test. Considerando che se non si sovrappongono, una modifica interromperà al massimo una sola prova.

  2. Solo perché pensavi a un modo migliore di scrivere un test, non sarei tornato lì e avrei iniziato a riscriverlo. Questo sta tornando alle persone che continuano a scrivere e riscrivere la stessa classe / funzione perché cercano di renderlo perfetto. Non sarà mai perfetto, quindi vai avanti. Quando scopri un metodo migliore, tienilo nella parte posteriore della tua mente (o aggiungi ai commenti del test). La prossima volta che ci sei, e vedi immediatamente il vantaggio di passare al nuovo modo, quello è il momento di rifattorizzare. Altrimenti, se la funzione è terminata e sei passato e tutto funziona, lascialo funzionare.

  3. TDD si concentra sulla fornitura di valore immediato, non semplicemente sulla garanzia che ogni funzione sia verificabile. Quando aggiungi funzionalità, inizia chiedendo "di cosa ha bisogno il client". Quindi definire un'interfaccia per fornire al cliente ciò di cui ha bisogno. Quindi implementare tutto ciò che serve per superare il test. TDD è quasi come testare scenari di casi d'uso (compresi tutti gli "what-ifs"), piuttosto che semplicemente codificare funzioni pubbliche e testare ciascuno di essi.

  4. Hai chiesto informazioni sul test del codice GUI. Cerca i modelli "Humble Dialog" e "MVVM". L'idea alla base di entrambi è che si crea un set di classi "view model", che in realtà non hanno una logica specifica dell'interfaccia utente. Tuttavia, queste classi avranno tutte le logiche aziendali che di solito fanno parte dell'interfaccia utente e queste classi dovrebbero essere verificabili al 100%. Ciò che rimane è una shell UI molto sottile e sì, in genere quella shell viene lasciata senza copertura del test, ma a quel punto non dovrebbe avere quasi alcuna logica.

  5. Se disponi di gran parte del codice esistente, come pochi altri hanno suggerito, non dovresti iniziare ad aggiungere test unitari dappertutto. Ti ci vorrà per sempre e non trarrai beneficio dall'aggiunta di unit test all'80% delle classi che sono stabili e non cambieranno nel prossimo (o non così vicino) futuro. Tuttavia, per i nuovi lavori, trovo estremamente utile l'utilizzo dello sviluppo TDD con TUTTO il codice. Non solo ti ritrovi con una suite con test automatici quando hai finito, ma lo sviluppo effettivo ha enormi vantaggi:

    • Considerando la testabilità, scriverai un codice che è meno accoppiato e più modulare
    • Considerando il tuo contratto pubblico prima di ogni altra cosa, finirai con interfacce pubbliche che sono molto più pulite
    • Durante la scrittura del codice, la verifica di nuove funzionalità richiede millisecondi rispetto all'esecuzione dell'intera applicazione e al tentativo di forzare l'esecuzione sulla strada giusta. Il mio team rilascia ancora un codice di gestione degli errori che non è stato nemmeno eseguito UNA VOLTA solo perché non sono riusciti a ottenere il giusto set di condizioni. È incredibile quanto tempo perdiamo quando in seguito queste condizioni si verificano. E sì, molto di questo codice è ciò che qualcuno avrebbe considerato "non un'area per molti cambiamenti in futuro una volta che il test del fumo sarà terminato".

1

Esistono alcuni metodi che non sono stati testati, vale a dire quei test. Tuttavia, c'è qualcosa da dire per alcuni test aggiunti dopo la scrittura del codice iniziale, come condizioni al contorno e altri valori in modo che possano esserci più test su un singolo metodo.

Mentre puoi testare eccessivamente il tuo codice, di solito arriva dove qualcuno vuole testare tutte le possibili permutazioni degli input che non sembrano proprio quello che stai facendo. Ad esempio, se hai un metodo che accetta un carattere, scrivi un test per ogni possibile valore che potrebbe essere inserito? Quello sarebbe il punto in cui verrai sottoposto a sovrastima, IMO.


Ah, ok Non è quello che sto facendo. Di solito finisco per pensare a una situazione diversa in cui potrei testare i miei metodi in qualche modo dopo che ho già effettuato il test iniziale. Mi stavo solo assicurando che valesse la pena fare quei test "extra", o se fosse finita.
cgasser,

Se lavori con incrementi abbastanza piccoli, di solito puoi essere ragionevolmente sicuro che il tuo test funzioni davvero. In altre parole, avere un test fallito (per la giusta ragione!) Sta di per sé testando il test. Ma quel livello di "ragionevolmente sicuro" non sarà alto come il codice sotto test.
Frank Shearar,

1

Generalmente lo stai facendo bene.

I test sono codice. Quindi, se riesci a migliorare il test, vai avanti e riformattalo. Se ritieni che un test possa essere migliorato, procedi e modificalo. Non aver paura di sostituire un test con uno migliore.

Ti consiglio di testare il tuo codice, evitando di specificare come il codice dovrebbe fare quello che sta facendo. I test dovrebbero esaminare i risultati dei metodi. Questo aiuterà con il refactoring. Alcuni metodi non hanno bisogno di essere testati esplicitamente (ad esempio semplici getter e setter) perché li userete per verificare i risultati di altri test.


Stavo scrivendo test anche per getter e setter, quindi grazie per quel suggerimento. Questo mi farà risparmiare un lavoro non necessario.
cgasser,

"Alcuni metodi non devono essere testati esplicitamente (ad esempio semplici getter e setter)" - Non hai mai copiato / incollato un getter e setter e hai dimenticato di cambiare il nome del campo dietro di esso? Il vantaggio del semplice codice è che richiede test semplici: quanto tempo stai risparmiando?
pdr,

Non intendo che il metodo non sia stato testato. Viene verificato semplicemente confermando che sono stati impostati altri metodi o durante l'impostazione effettiva di un test. Se il getter o setter non funziona correttamente, il test fallirà perché le proprietà non sono state impostate correttamente. Li fai testare gratuitamente, implicitamente.
Schleis,

I test di getter e setter non richiedono molto tempo, quindi probabilmente continuerò a farli. Tuttavia, non copio e incollo mai il mio codice, quindi non ho riscontrato questo problema.
cgasser,

0

La mia opinione su TDD è che gli strumenti hanno creato un mondo di sviluppatori in stile "punta e clicca". Solo perché gli strumenti creano uno stub di test per ogni metodo non significa che dovresti scrivere test per ogni metodo. Alcune persone stanno "rinominando" TDD come BDD (sviluppo guidato dal comportamento) in cui i test sono molto più granulari e hanno lo scopo di testare il comportamento della classe, non ogni metodo poco complicato.

Se progetti i tuoi test per testare la classe come previsto, allora inizi a ottenere alcuni benefici, specialmente quando inizi a scrivere test che si esercitano un po 'più di ogni metodo, specialmente quando inizi a testare l'interazione di quelli metodi. Suppongo che potresti pensarlo come scrivere test per una classe, piuttosto che metodi. In ogni caso, è comunque necessario scrivere "test di accettazione" che esercitano la combinazione di metodi per assicurarsi che non vi siano contraddizioni o conflitti nel modo in cui vengono utilizzati insieme.

Non confondere TDD con i test - non lo è. TDD è progettato in modo da scrivere codice per esercitare le proprie esigenze, non per testare i metodi. È un punto sottile ma importante che spesso si perde nelle persone che scrivono ciecamente un codice di prova per ogni metodo. È che dovresti scrivere test che assicurino che il tuo codice faccia quello che vuoi che faccia, non che il codice che hai scritto funzioni come dovrebbe.

Ci sono alcuni buoni collegamenti a destra su BDD v TDD. Dai un'occhiata.


0

Quando inizi a imparare il TDD, sì, dovresti seguire ciecamente l'approccio dogmatico di non scrivere una singola riga di codice se non per fare un test fallito e scrivere solo un test sufficiente per fallire (e fallire per il motivo giusto / previsto) .

Dopo aver appreso di cosa parla TDD, allora puoi decidere che alcuni tipi di cose non valgono la pena di essere testate. Questo è lo stesso approccio che dovresti seguire per tutto, e le arti marziali giapponesi chiamano questo " shuhari ". (Il link spiega anche come si può progredire attraverso le fasi dell'apprendimento senza un insegnante che è, sospetto, come la maggior parte delle persone deve imparare.)


0

Credo che tu stia verificando.

Pratico TDD da molti anni e, nella mia esperienza, quando TDD viene eseguito in modo efficace, ottieni due vantaggi principali:

  • Fornire un feedback rapido
  • Abilita refactoring

Fornire un feedback rapido

Soprattutto con i linguaggi dinamici, posso eseguire i test pertinenti in meno di un secondo. E ho osservatori di file system che eseguono questi test automaticamente quando un file sorgente viene modificato sul disco. Quindi praticamente non ho tempo di attesa per i test e so immediatamente se il codice che scrivo ha funzionato come previsto. Pertanto il TDD conduce a un modo di lavorare molto efficiente.

Abilita refactoring

Se disponi di una buona suite di test, puoi effettuare il refactoring in modo sicuro, man mano che acquisisci nuove informazioni su come dovrebbe essere progettato il sistema.

Una buona suite di test ti consente di spostare la responsabilità nel tuo codice e avere ancora la certezza che il codice funzioni come previsto dopo lo spostamento. E dovresti essere in grado di farlo con piccole modifiche al codice di test.

Se scrivi test per ogni metodo nel tuo sistema, allora è probabile che non puoi facilmente refactificare il tuo codice, ogni refactor del tuo codice richiederà enormi modifiche al codice di test. E puoi anche essere sicuro che il codice di test funzioni ancora come previsto? Oppure hai introdotto accidentalmente un bug nel codice di test, che di conseguenza porta a un bug nel codice di produzione?

Se tuttavia, come suggerito anche nella risposta di pdr , ti concentri sul comportamento anziché sui metodi durante la scrittura dei test, avrai dei test che richiederanno molte meno modifiche durante il refactoring del sistema.

O come dice Ian Cooper in questa presentazione (ho citato dalla memoria, quindi potrebbe non essere citato correttamente):

Il motivo per cui scrivi un nuovo test dovrebbe essere l'aggiunta di un nuovo comportamento, non l'aggiunta di una nuova classe


-2

Dovresti testare ogni metodo pubblico .

Il trucco qui è che se i tuoi metodi pubblici sono molto piccoli probabilmente stai esponendo troppe informazioni. La pratica comune di esporre ogni proprietà in quanto getXXX()rompe effettivamente l'incapsulamento.

Se i tuoi metodi pubblici sono effettivamente il comportamento della classe, allora dovresti testarli. In caso contrario, non sono buoni metodi pubblici.

EDIT: la risposta di pdr è molto più completa della mia.

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.