TDD: Lo sto facendo bene?

14

Sono un nuovo programmatore (sto imparando da circa un anno) e nel mio obiettivo di diventare migliore ho appena imparato a conoscere TDD. Volevo prendere l'abitudine di usarlo poiché sembra molto utile. Volevo controllare e assicurarmi che lo stia usando correttamente.

Che cosa sto facendo:

  1. Pensa a un nuovo metodo di cui ho bisogno.
  2. Crea un test per tale metodo.
  3. Fail test.
  4. Metodo di scrittura.
  5. superare il test.
  6. Metodo di rifattore
  7. Ripeti.

Lo sto facendo per OGNI metodo che scrivo, ce ne sono alcuni con cui non dovrei preoccuparmi? In seguito, di solito penso a un modo per testare i miei metodi già esistenti in un modo o in una situazione diversa. Dovrei fare questi nuovi test a cui penso, o dato che ogni metodo ha già un test da solo non dovrei preoccuparmi? Posso fare OLTRE testare il mio codice, credo che sia la mia preoccupazione principale nel chiedere questo.

Modifica

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

    
posta cgasser 18.05.2012 - 19:52
fonte

9 risposte

16

Ciò che descrivi come un flusso di lavoro non è secondo me lo Spirito di TDD.

La sinossi del libro di Kent Becks su Amazon dice:

Quite simply, test-driven development is meant to eliminate fear in application development. While some fear is healthy (often viewed as a conscience that tells programmers to "be careful!"), the author believes that byproducts of fear include tentative, grumpy, and uncommunicative programmers who are unable to absorb constructive criticism. When programming teams buy into TDD, they immediately see positive results. They eliminate the fear involved in their jobs, and are better equipped to tackle the difficult challenges that face them. TDD eliminates tentative traits, it teaches programmers to communicate, and it encourages team members to seek out criticism However, even the author admits that grumpiness must be worked out individually! In short, the premise behind TDD is that code should be continually tested and refactored.

TDD pratico

I test automatizzati formali, in particolare i test unitari di ogni metodo di ogni classe, sono un cattivo anti-modello e non provano nulla. C'è un equilibrio da avere. Stai scrivendo unit test per ogni metodo setXXX/getXXX , sono anche metodi!

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

Come in tutto questo, esiste 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 è un codice che è fondamentale per la logica aziendale e soggetto a frequenti modifiche in base ai requisiti in evoluzione. Quelle cose necessitano di test formali automatizzati, sarebbe un grosso ritorno sull'investimento.

Sarai molto difficile trovare anche molti negozi professionali che funzionano così. Semplicemente non ha senso per gli affari spendere soldi per testare cose che per tutti gli scopi pratici non cambieranno mai dopo che un semplice test del fumo è stato eseguito. La scrittura di test di unità automatizzate formali per metodi .getXXX/.setXXX ne è un primo esempio, completa perdita di tempo.

It is now two decades since it was pointed out that program testing may convincingly demonstrate the presence of bugs, but can never demonstrate their absence. After quoting this well-publicized remark devoutly, the software engineer returns to the order of the day and continues to refine his testing strategies, just like the alchemist of yore, who continued to refine his chrysocosmic purifications.

-- Edsger W. Djikstra. (Written in 1988, so it's now closer to 4.5 decades.)

Vedi anche questa risposta .

    
risposta data 18.05.2012 - 20:18
fonte
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 questo comportamento.
  3. Fail test.
  4. Scrivi nuovo o estendi il metodo esistente.
  5. superare il test.
  6. Codice del refactore
  7. Ripeti.

Non creare automaticamente getter e setter per ogni proprietà . Non pensare a un intero metodo e scrivi il test ( s) per coprire tutte le funzionalità . Prova a incapsulare le proprietà all'interno della classe e scrivi i metodi per fornire il comportamento di cui hai bisogno. 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 agli altri processi di progettazione sta lasciando dietro di sé un flusso di test di regressione automatizzati, piuttosto che un pezzo di carta che si butta nel cestino.

Ricorda inoltre le tre regole di TDD dello zio Bob .

  1. Non sei autorizzato a scrivere alcun codice di produzione a meno che non si debba effettuare un passaggio di prova dell'unità in errore.
  2. Non è consentito scrivere più di un test unitario di quanto sia sufficiente per fallire; e gli errori di compilazione sono errori.
  3. Non ti è permesso scrivere più codice di produzione di quanto sia sufficiente per superare il test dell'unità fallita.
risposta data 18.05.2012 - 20:28
fonte
5

Poche cose da aggiungere alle risposte di altri:

  1. C'è una cosa come il test. Vuoi assicurarti che i test dell'unità si sovrappongano il meno possibile. Non ha senso avere più test per verificare le stesse condizioni nello stesso pezzo di codice. D'altra parte, quando si rifatta il codice di produzione e si hanno molti test che si sovrappongono a quella sezione, sarà necessario tornare indietro e correggere tutti i test. Considerando che se non si sovrappongono, una modifica interromperà al massimo un solo test.

  2. Solo perché hai pensato a un modo migliore di scrivere un test, non tornerei indietro e inizierei 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 nel retro della mente (o aggiungi ai commenti del test). La prossima volta che ci sei, e vedi subito il beneficio di passare al nuovo modo, questo è il momento del refactoring. Altrimenti, se la funzione è terminata e sei andato avanti e tutto funziona, lascialo funzionare.

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

  4. Hai chiesto di testare il codice della GUI. Cerca i modelli "Humble Dialog" e "MVVM". L'idea alla base di entrambi è la creazione di un insieme di classi "view model", che in realtà non hanno una logica specifica dell'interfaccia utente. Tuttavia, queste classi avranno tutta la logica aziendale che tipicamente fa parte della tua interfaccia utente e queste classi dovrebbero essere testabili al 100%. Ciò che rimane è una shell UI molto sottile e sì, in genere quella shell viene lasciata senza copertura di test, ma a quel punto non dovrebbe avere quasi nessuna logica.

  5. Se hai una gran parte del codice esistente, come pochi altri hanno suggerito, non dovresti iniziare ad aggiungere test unitari assolutamente ovunque. Ti prenderà per sempre e non otterrai benefici dall'aggiunta di test unitari all'80% delle classi che sono stabili e non cambieranno nel futuro prossimo (o non così vicino). Tuttavia, per un nuovo lavoro, trovo che usare lo sviluppo TDD con TUTTO il codice sia estremamente utile. Non appena ti ritroverai con una suite con test automatici quando hai finito, ma lo sviluppo effettivo ha enormi vantaggi:

    • Considerando la testabilità, si scriverà un codice che è meno accoppiato e più modulare
    • Considerando il tuo contratto pubblico prima di ogni altra cosa, ti ritroverai con interfacce pubbliche molto più pulite
    • Mentre scrivi il codice, la verifica della nuova funzionalità richiede millisecondi rispetto all'esecuzione dell'intera applicazione e cerca di forzare l'esecuzione verso il basso nel percorso corretto. Il mio team rilascia ancora il codice di gestione degli errori che non è stato ancora eseguito UNA VOLTA solo perché non è stato possibile ottenere il set di condizioni appropriato. È incredibile quanto tempo sprechiamo quando in QA succedono queste condizioni. E sì, un sacco di questo codice è ciò che qualcuno avrebbe considerato "non un'area per molti cambiamenti in futuro una volta che i test del fumo sono stati fatti".
risposta data 18.05.2012 - 22:43
fonte
1

Esistono alcuni metodi che non vengono testati, ovvero quei test. Tuttavia, c'è qualcosa da dire per alcuni test che vengono aggiunti dopo che il codice iniziale è stato scritto, come le condizioni al contorno e altri valori in modo che ci possano essere più test su un singolo metodo.

Anche se puoi testare il tuo codice, solitamente viene dove qualcuno vuole testare ogni possibile permutazione degli input che non suona come quello che stai facendo. Ad esempio, se hai un metodo che accetta un personaggio, scrivi un test per ogni possibile valore che potrebbe essere inserito? Questo sarebbe il posto in cui verrai soprainteso, IMO.

    
risposta data 18.05.2012 - 19:59
fonte
1

In genere lo stai facendo bene.

I test sono codice. Quindi se puoi migliorare il test, vai avanti e rifattalo. Se pensi che un test possa essere migliorato, fallo e cambialo. Non aver paura di sostituire un test con uno migliore.

Ti consiglio di testare il tuo codice, evita di specificare come il codice dovrebbe fare ciò che sta facendo. I test dovrebbero esaminare i risultati dei metodi. Questo aiuterà con il refactoring. Alcuni metodi non devono essere testati esplicitamente (cioè getter e setter semplici) perché li userete per verificare i risultati di altri test.

    
risposta data 18.05.2012 - 20:06
fonte
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" il TDD come BDD (sviluppo guidato dal comportamento) in cui i test sono molto più grossi e destinati a testare il comportamento della classe, non ogni piccolo metodo.

Se si progettano i test per testare la classe come destinata a essere utilizzata, si iniziano a ottenere alcuni vantaggi, soprattutto quando si iniziano a scrivere test che esercitano un po 'più di ogni metodo, specialmente quando si inizia a testare la interazione di questi metodi. Suppongo che potresti pensarlo come un test di scrittura per una classe, piuttosto che come metodi. In ogni caso, devi comunque scrivere 'test di accettazione' che esercitino la combinazione di metodi per assicurarti che non ci siano contraddizioni o conflitti nel modo in cui vengono usati insieme.

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

Ci sono alcuni buoni link a destra su BDD v TDD. Dategli un'occhiata.

    
risposta data 18.05.2012 - 20:16
fonte
0

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

Una volta che hai imparato cos'è TDD, POI puoi decidere che certi tipi di cose non valgono la pena di essere testati. Questo è lo stesso approccio che dovresti seguire per tutto, e le arti marziali giapponesi chiamano questo " shuhari ". (Il link spiega anche come si possa progredire attraverso le fasi dell'apprendimento senza un insegnante che, sospetto, è il modo in cui la maggior parte delle persone deve imparare.)

    
risposta data 22.05.2012 - 12:02
fonte
0

Credo che tu stia testando troppo.

Ho praticato TDD per molti anni e, secondo la mia esperienza, quando il TDD viene eseguito in modo efficace, ottieni due vantaggi principali:

  • Fornisci un feedback rapido
  • Abilita refactoring

Fornisci feedback rapidi

In particolare con i linguaggi dinamici, posso eseguire i test pertinenti in meno di un secondo. E ho gli osservatori del file system che eseguono questi test automaticamente quando un file sorgente viene modificato su disco. Quindi non ho praticamente nessun tempo di attesa per i test e so immediatamente se il codice che scrivo ha funzionato come previsto. Quindi TDD porta a un modo di lavorare molto efficace.

Abilita refactoring

Se disponi di una buona suite di test, puoi rifattorici in tutta sicurezza, acquisendo nuove conoscenze sul modo in cui il sistema dovrebbe essere progettato.

Una buona suite di test consente di spostare la responsabilità nel codice e di 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 le probabilità sono che non puoi facilmente refactoring il tuo codice, ogni refactoring del tuo codice richiederà enormi modifiche al codice di test. E puoi persino essere sicuro che il codice di prova funzioni ancora come previsto? O hai accidentalmente introdotto un bug nel codice di test, che di conseguenza porta a un bug nel codice di produzione?

Se tuttavia, come suggerito anche nella risposta del pdr , concentrati su comportamento invece di metodi durante la scrittura dei test, avrai 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 correttamente citata):

Your reason for writing a new test should be adding a new behavior, not adding a new class

    
risposta data 25.07.2017 - 08:39
fonte
-2

Dovresti testare ogni metodo pubblico .

Il problema è che se i tuoi metodi pubblici sono molto piccoli, probabilmente stai esponendo troppe informazioni. La pratica comune di esporre ogni proprietà come getXXX() interrompe effettivamente l'incapsulamento.

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

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

    
risposta data 18.05.2012 - 22:20
fonte

Leggi altre domande sui tag