Perché non scrivere tutti i test contemporaneamente quando si fa TDD?

52

Il ciclo Red-Green - Refactor per TDD è ben definito e accettato. Scriviamo one in mancanza di test unitario e lo facciamo passare il più semplicemente possibile. Quali sono i vantaggi di questo approccio rispetto alla scrittura di molti test unitari guasti per una classe e li fanno passare tutti in una volta.

La suite di test ti protegge ancora contro la scrittura di codice errato o errori nella fase di refactoring, quindi qual è il danno? A volte è più facile scrivere tutti i test per una classe (o modulo) prima come una forma di "brain dump" per scrivere rapidamente tutto il comportamento previsto in un colpo solo.

    
posta RichK 02.04.2012 - 15:32
fonte

16 risposte

45

Il design guidato dal test riguarda il fatto che la tua API sia corretta, non il codice.

Il vantaggio di scrivere i test di failing più semplici è che ottieni la tua API (che essenzialmente stai progettando al volo) il più semplice possibile. Davanti.

Qualsiasi utilizzo futuro (che i prossimi test scritti sono) passerà dal design semplice iniziale, invece di un design subottimale che affronta casi più complessi.

    
risposta data 02.04.2012 - 14:12
fonte
72

Quando scrivi un test one , ti concentri sulla una cosa.
Con molti test hai diffuso la tua attenzione su molte attività, quindi non è una buona idea.

    
risposta data 02.04.2012 - 12:29
fonte
25

Una delle difficoltà durante la scrittura di unit test è che si sta scrivendo codice e che di per sé può potenzialmente essere soggetto a errori. C'è anche la possibilità che tu possa finire per dover cambiare i tuoi test più tardi come risultato di uno sforzo di refactoring mentre scrivi il tuo codice di implementazione. Con TDD, questo significa che potresti finire per essere un po 'troppo portato via con i tuoi test e trovare te stesso che ha bisogno di riscrivere un sacco di codice di test essenzialmente "non testato" mentre la tua implementazione matura nel corso del progetto. Un modo per evitare questo tipo di problema è concentrarsi semplicemente sul fare una singola cosa alla volta. Ciò ti garantisce di ridurre al minimo l'impatto di eventuali modifiche ai test.

Ovviamente questo dipenderà in gran parte dal modo in cui scrivi il tuo codice di test. Stai scrivendo un test unitario per ogni singolo metodo, o stai scrivendo dei test che si concentrano su caratteristiche / requisiti / comportamenti? Un altro approccio potrebbe essere quello di utilizzare un approccio basato sul comportamento con un framework adatto e concentrarsi sulla scrittura di test come se fossero specifiche. Ciò significherebbe adottare il metodo BDD o adattare i test BDD se si desidera attenersi più formalmente al TDD. In alternativa, è possibile attenersi interamente al paradigma TDD, ma modificare il modo in cui si scrivono i test in modo che invece di concentrarsi interamente sui metodi di test individualmente, si testano i comportamenti in generale come un mezzo per soddisfare le specifiche delle caratteristiche dei requisiti che si stanno implementando.

Indipendentemente dall'approccio specifico che prendi, in tutti i casi che ho descritto sopra utilizzi un approccio test-first, quindi mentre potresti essere tentato di scaricare semplicemente il tuo cervello in una deliziosa suite di test, anche tu vuoi combattere la tentazione di fare più di quanto sia assolutamente necessario. Ogni volta che sto per iniziare una nuova suite di test, ricomincio a ripetere YAGNI a me stessa, a volte persino a inserire un commento nel mio codice per ricordarmi di rimanere concentrato su ciò che è immediatamente importante e di fare solo il minimo richiesto per soddisfare il requisiti della funzione che sto per implementare. Attenersi a Red-Green-Refactor aiuta a garantire che lo farai.

    
risposta data 26.04.2012 - 02:05
fonte
16

Penso che facendo questo, perdi il processo di TDD. Semplicemente scrivendo tutti i tuoi test all'inizio non stai veramente passando attraverso il processo di sviluppo usando TDD. Stai semplicemente indovinando in anticipo quali test hai bisogno. Questo sarà un insieme di test molto diverso da quelli che finisci per scrivere se li fai uno alla volta mentre sviluppi il tuo codice. (A meno che il tuo programma non sia di natura banale.)

    
risposta data 02.04.2012 - 14:33
fonte
10

L'idea alla base di TDD è una rapida iterazione.

Se disponi di numerosi passaggi di test che devono essere scritti prima di dover scrivere il codice, è difficile ridefinire il codice in modo iterativo.

Senza il refactoring del codice facile perdi molti vantaggi di TDD.

    
risposta data 29.11.2013 - 14:56
fonte
9

Io "scrivo" tutti i test che riesco a pensare in anticipo mentre "brain storming", tuttavia scrivo ogni test come un singolo commento che descrive il test.

Quindi converto un test in codice e faccio il lavoro in modo che compili e passi . Spesso decido che non ho bisogno di tutti i test che pensavo di aver fatto, o ho bisogno di test diversi, questa informazione viene solo dalla scrittura del codice per far passare i test.

Il problema è che non puoi scrivere un test in codice finché non hai creato il metodo e le classi testate, altrimenti otterrai solo numerosi errori del compilatore che ti aiutano a lavorare su un singolo test alla volta.

Ora, se stai utilizzando un sistema come il flusso delle specifiche quando i test sono scritti in "inglese" potresti desiderare di far accettare ai clienti un insieme di test mentre hai il loro tempo, invece di creare un solo test.

    
risposta data 02.04.2012 - 13:50
fonte
5

Nella mia (limitata) esperienza con TDD, posso dirti che ogni tempo in cui ho infranto la disciplina di scrivere un test alla volta, le cose sono andate male. È una trappola facile a cui cadere. "Oh, quel metodo è banale," pensi a te stesso, "quindi butto fuori questi due altri test correlati e continueremo a muoverti." Bene, indovina cosa? Niente è così banale come sembra. Ogni volta che mi sono imbattuto in questa trappola, ho finito per eseguire il debug di qualcosa che pensavo era facile, ma ho avuto strani casi d'angolo. E da quando ho iniziato a scrivere diversi test in una volta, è stato un sacco di lavoro per rintracciare dov'era il bug.

Se hai bisogno di un dump di informazioni sul cervello, hai molte opzioni:

  • Lavagna
  • User story
  • Commenti
  • Carta e penna di buon vecchio

Si noti che da nessuna parte in questa lista è il compilatore. : -)

    
risposta data 02.04.2012 - 15:43
fonte
5

Supponi di sapere come sarà il tuo codice prima di scriverlo. TDD / BDD è tanto un processo di progettazione / scoperta quanto un processo di QA. Per una determinata funzione, si scrive il test più semplice che verificherebbe che la funzione sia soddisfatta (a volte ciò potrebbe richiedere diversi motivi a causa della complessità di una funzione). Quel primo test che scrivi è caricato con ipotesi su come sarà il codice di lavoro. Se scrivi l'intera suite di test prima di scrivere la prima riga di codice per supportarla, stai facendo una litania di ipotesi non verificate. Invece, scrivi un'ipotesi e verificala. Quindi scrivi il prossimo. Nel processo di verifica della prossima ipotesi, potresti semplicemente interrompere un'ipotesi precedente in modo da dover tornare indietro e modificare quella prima ipotesi in modo che corrisponda alla realtà o cambiare la realtà in modo che la prima ipotesi si applichi ancora.

Pensa a ciascun test di unità che scrivi come teoria in un quaderno scientifico. Mentre compili il taccuino, provi le tue teorie e ne formi di nuove. A volte provare una nuova teoria confuta una teoria precedente, quindi devi risolverla. È più facile dimostrare una teoria alla volta piuttosto che provare a dimostrare di dire 20 in una volta.

    
risposta data 02.04.2012 - 15:51
fonte
4

Il TDD è un approccio altamente iterativo, che (nella mia esperienza) si adatta meglio alle modalità di sviluppo del mondo reale. Di solito la mia implementazione prende forma gradualmente durante questo processo e ogni fase può portare ulteriori domande, approfondimenti e idee per il test. Questo è l'ideale per mantenere la mia mente concentrata sul compito reale ed è molto efficiente perché ho solo bisogno di mantenere un numero limitato di cose nella memoria a breve termine in qualsiasi momento. Questo a sua volta riduce la possibilità di errori.

La tua idea è fondamentalmente un approccio Big Test Up Front, che IMHO è più difficile da gestire e può diventare più dispendioso. Che cosa succede se ti rendi conto a metà del tuo lavoro che il tuo approccio non è buono, che la tua API è imperfetta e devi ricominciare da capo o utilizzare una libreria di terze parti? Quindi gran parte del lavoro svolto per scrivere i tuoi test in anticipo diventa uno sforzo inutile.

Detto questo, se questo funziona per te, bene. Posso immaginare che se lavori su specifiche tecniche fisse e dettagliate, su un dominio di cui sei profondamente esperto e / o su un compito abbastanza piccolo, puoi avere la maggior parte o tutti i casi di test necessari pronti e la tua implementazione chiara già da la partenza. Quindi potrebbe essere sensato iniziare scrivendo tutti i test contemporaneamente. Se la tua esperienza è che questo ti rende più produttivo a lungo termine, non devi preoccuparti troppo dei libri di regole: -)

    
risposta data 02.04.2012 - 12:36
fonte
4

Oltre a pensare solo a una cosa, un paradigma di TDD è scrivere il codice minimo possibile per superare il test. Quando scrivi un test alla volta, è molto più facile vedere il percorso per scrivere un codice sufficiente per far passare quel test. Con un'intera serie di test da superare, non si arriva al codice a piccoli passi, ma bisogna fare un grande salto per farli passare tutti in una volta.

Ora, se non ti limiti a scrivere il codice per farli passare tutti "in una volta sola", ma scrivi abbastanza codice per passare un test alla volta, potrebbe comunque funzionare. Dovresti avere più disciplina per non andare avanti e scrivere più codice di quello che ti serve, però. Una volta iniziato questo percorso, ti lasci aperto a scrivere più codice di quello che descrivono i test, che può essere non testato , almeno nel senso che non è guidato da un test e forse nel percepire che non è necessario (o esercitato) da alcun test.

Scoprire cosa dovrebbe fare il metodo, come commenti, storie, una specifica funzionale, ecc., è perfettamente accettabile. Aspetterei di tradurre questi in prove uno alla volta però.

L'altra cosa che puoi perdere scrivendo i test tutto in una volta è il processo di pensiero attraverso il quale superare un test può indurti a pensare ad altri casi di test. Senza una banca di test esistenti, è necessario pensare al prossimo caso di test nel contesto dell'ultimo test di passaggio. Come ho detto, avere una buona idea di ciò che il metodo dovrebbe fare è molto buono, ma molte volte mi sono trovato a trovare nuove possibilità che non avevo considerato a priori, ma che si sono verificate solo nel processo di scrittura del test. C'è il pericolo che tu possa perdere queste cose a meno che tu non prenda l'abitudine di pensare a quali nuovi test posso scrivere che non ho già.

    
risposta data 02.04.2012 - 15:08
fonte
3

Ho lavorato a un progetto in cui gli sviluppatori che hanno scritto i test (in mancanza) erano diversi dagli sviluppatori che implementavano il codice necessario per farli passare e l'ho trovato davvero efficace.

In questo caso, solo i test relativi all'iterazione corrente sono stati scritti una volta. Quindi quello che suggerisci è perfettamente possibile in questo tipo di scenario.

    
risposta data 02.04.2012 - 13:37
fonte
2
  • Quindi provi a concentrarti su troppe cose alla volta.
  • Durante l'implementazione per far passare tutti i test non hai alcuna versione funzionante della tua applicazione. Se devi implementare molto, non avrai nessuna versione funzionante per molto tempo.
risposta data 02.04.2012 - 12:32
fonte
2

Il ciclo Red-Green-Refactor è una checklist destinata agli sviluppatori nuovi di TDD. Direi che è una buona idea seguire questa lista di controllo fino a quando non si sa quando seguirla e quando è possibile interromperla (ovvero fino a quando non si è sicuri di dover fare questa domanda su StackOverflow:)

Avendo fatto TDD per quasi un decennio, posso dirvi che molto raramente, se non mai, scrivo molti test falliti prima di scrivere codice di produzione.

    
risposta data 02.04.2012 - 13:24
fonte
1

Stai descrivendo BDD, dove alcuni stakeholder esterni hanno una specifica eseguibile. Ciò può essere utile se esiste una specifica anticipata predeterminata (ad esempio una specifica di formato, uno standard industriale o se il programmatore non è l'esperto di dominio).

L'approccio normale è quindi quello di coprire gradualmente sempre più test di accettazione, ovvero i progressi visibili al project manager e al cliente.

Di solito questi test sono specificati ed eseguiti in un framework BDD come Cucumber, Fitnesse o altri.

Tuttavia, questo non è qualcosa che si confonde con i tuoi test unitari, che sono molto più vicini ai dettagli di implementazione nitty grintosi con un sacco di casi limite relativi alle API, problemi di inizializzazione ecc. strongmente focalizzati sull'elemento sotto test , che è un artefatto di implementazione .

La disciplina red-green-refactor ha molti vantaggi, e l'unico vantaggio che puoi sperare digitandoli in anticipo è il pareggio.

    
risposta data 02.04.2012 - 12:48
fonte
1

Un test alla volta: il vantaggio principale è l'attenzione su una cosa. Pensa al design approfondito: puoi andare in profondità e rimanere concentrato con un ciclo di feedback rapido. Tuttavia, potresti perdere lo scopo dell'intero problema! Questo è il momento in cui entra in gioco il grande refactoring. Senza di esso TDD non funziona.

Tutti i test: l'analisi e la progettazione possono rivelare di più l'ambito del problema. Pensa al design di ampiezza. Si analizza il problema da più angolazioni e si aggiunge input dall'esperienza. È intrinsecamente più difficile, ma può produrre un vantaggio interessante - meno refactoring - se ne fai "abbastanza". Attenzione, è facile da sovra-analizzare e ancora perdere completamente il bersaglio!

Trovo difficile consigliare generalmente di preferire l'uno o l'altro, perché i fattori sono molti: esperienza (soprattutto con lo stesso problema), conoscenza e abilità del dominio, cordialità del codice per il refactoring, complessità del problema ...

Immagino che se ci concentrassimo più strettamente sulle tipiche applicazioni aziendali, TDD con il suo approccio di errore più rapido e quasi alla prova di solito vincerebbe in termini di efficacia.

    
risposta data 02.04.2012 - 13:52
fonte
1

Supponendo che il framework di test lo supporti, ciò che suggerirei è che invece di implementare i test che si desidera eseguire, inserire invece test descrittivi in attesa che verranno implementati in seguito. Ad esempio, se l'API deve eseguire foo e bar ma non biz, basta aggiungere il seguente codice (questo esempio è in rspec) per la tua suite di test, quindi attaccarli uno per uno. Tieni giù i pensieri velocemente e puoi risolvere tutti i tuoi problemi uno per uno. Quando passeranno tutti i test, saprai quando avrai risolto tutti i tuoi problemi durante il tuo braindump.

describe "Your API" do

  it "should foo" do
    pending "braindump from 4/2"
  end

  it "should bar" do
    pending "braindump from 4/2"
  end

  it "should not biz" do
    pending "braindump from 4/2"
  end

end
    
risposta data 02.04.2012 - 17:33
fonte

Leggi altre domande sui tag