Quando scrivi il codice "reale" in TDD?


147

Tutti gli esempi che ho letto e visto nei video di formazione hanno esempi semplicistici. Ma cosa non vedo se come faccio il codice "reale" dopo che divento verde. È questa la parte "Refactor"?

Se ho un oggetto abbastanza complesso con un metodo complesso, e scrivo il mio test e il minimo indispensabile per farlo passare (dopo che fallisce per la prima volta, rosso). Quando torno e scrivo il codice reale? E quanto codice reale scrivo prima di ripetere il test? Immagino che l'ultimo sia più intuizione.

Modifica: grazie a tutti coloro che hanno risposto. Tutte le tue risposte mi hanno aiutato immensamente. Sembra che ci siano idee diverse su ciò che stavo chiedendo o confuso, e forse c'è, ma quello che stavo chiedendo era, diciamo che ho una domanda per costruire una scuola.

Nel mio progetto, ho un'architettura con cui voglio iniziare, User Stories, e così via. Da qui, prendo quelle User Story e creo un test per testare User Story. L'utente dice: abbiamo persone che si iscrivono alla scuola e paghiamo le tasse di registrazione. Quindi, penso a un modo per farlo fallire. Nel fare ciò, progetto una classe di test per la classe X (forse Student), che fallirà. Quindi creo la classe "Studente". Forse "Scuola" non lo so.

Ma, in ogni caso, TD Design mi sta costringendo a riflettere sulla storia. Se riesco a far fallire un test, so perché non riesce, ma questo presuppone che posso farcela. Riguarda la progettazione.

Ho paragonato questo a pensare alla ricorsione. La ricorsione non è un concetto difficile. Potrebbe essere più difficile tenerne traccia nella tua testa, ma in realtà, la parte più difficile è sapere, quando la ricorsione "si rompe", quando fermarsi (la mia opinione, ovviamente.) Quindi devo pensare a ciò che si ferma prima la ricorsione. È solo un'analogia imperfetta e presuppone che ogni iterazione ricorsiva sia un "passaggio". Ancora una volta, solo un'opinione.

Nell'attuazione, la scuola è più difficile da vedere. I registri numerici e bancari sono "facili", nel senso che è possibile utilizzare l'aritmetica semplice. Riesco a vedere a + b e restituire 0, ecc. Nel caso di un sistema di persone, devo pensare di più su come implementarlo . Ho il concetto di fail, pass, refactor (principalmente a causa dello studio e di questa domanda).

Ciò che non so è basato sulla mancanza di esperienza, secondo me. Non so come fallire la registrazione di un nuovo studente. Non so come fallire qualcuno che digita un cognome e viene salvato in un database. So come creare un + 1 per la matematica semplice, ma con entità come una persona, non so se sto solo testando per vedere se ottengo un ID univoco del database o qualcos'altro quando qualcuno inserisce un nome in un database o entrambi o nessuno dei due.

O forse questo dimostra che sono ancora confuso.


193
Dopo il TDD la gente torna a casa per la notte.
Hobbs

14
Perché pensi che il codice che hai scritto non sia reale?
Goyo,

2
@RubberDuck Più delle altre risposte. Sono sicuro che mi riferirò presto. È ancora un po 'straniero, ma non mi arrenderò. Quello che hai detto aveva un senso. Sto solo cercando di dare un senso al mio contesto o ad una normale applicazione aziendale. Forse un sistema di inventario o simili. Devo considerarlo Sono grato per il tuo tempo però. Grazie.
johnny,

1
Le risposte hanno già colpito l'unghia sulla testa, ma fino a quando tutti i test passano e non hai bisogno di nuovi test / funzionalità, si può presumere che il codice che hai sia finito, barra che sfilaccia.
ESR,

3
C'è una supposizione nella domanda che può essere problematica in "Ho un oggetto abbastanza complesso con un metodo complesso". In TDD scrivi prima i test in modo da iniziare con un codice abbastanza semplice. Ciò ti costringerà a codificare una struttura di prova che dovrà essere modulare. Verrà creato un comportamento così complesso combinando oggetti più semplici. Se
finisci

Risposte:


243

Se ho un oggetto abbastanza complesso con un metodo complesso, e scrivo il mio test e il minimo indispensabile per farlo passare (dopo che fallisce per la prima volta, rosso). Quando torno e scrivo il codice reale? E quanto codice reale scrivo prima di ripetere il test? Immagino che l'ultimo sia più intuizione.

Non "tornare indietro" e scrivere "codice reale". È tutto vero codice. Quello che fai è tornare indietro e aggiungere un altro test che ti costringe a cambiare il tuo codice per fare passare il nuovo test.

Quanto al codice che scrivi prima di ripetere il test? Nessuna. Scrivi zero codice senza un test fallito che ti costringe a scrivere più codice.

Notare lo schema?

Camminiamo attraverso (un altro) semplice esempio nella speranza che aiuti.

Assert.Equal("1", FizzBuzz(1));

Peazy facile.

public String FizzBuzz(int n) {
    return 1.ToString();
}

Non è quello che chiameresti vero codice, giusto? Aggiungiamo un test che forza un cambiamento.

Assert.Equal("2", FizzBuzz(2));

Potremmo fare qualcosa di stupido if n == 1, ma passeremo alla soluzione sana.

public String FizzBuzz(int n) {
    return n.ToString();
}

Freddo. Funzionerà con tutti i numeri non FizzBuzz. Qual è il prossimo input che costringerà il codice di produzione a cambiare?

Assert.Equal("Fizz", FizzBuzz(3));

public String FizzBuzz(int n) {
    if (n == 3)
        return "Fizz";
    return n.ToString();
}

E di nuovo. Scrivi un test che non supererà ancora.

Assert.Equal("Fizz", FizzBuzz(6));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    return n.ToString();
}

E ora abbiamo coperto tutti i multipli di tre (che non sono anche multipli di cinque, lo noteremo e torneremo).

Non abbiamo ancora scritto un test per "Buzz", quindi scriviamolo.

Assert.Equal("Buzz", FizzBuzz(5));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    if (n == 5)
        return "Buzz"
    return n.ToString();
}

E ancora, sappiamo che c'è un altro caso che dobbiamo gestire.

Assert.Equal("Buzz", FizzBuzz(10));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    if (n % 5 == 0)
        return "Buzz"
    return n.ToString();
}

E ora possiamo gestire tutti i multipli di 5 che non sono anche multipli di 3.

Fino a questo punto, abbiamo ignorato il passaggio del refactoring, ma vedo qualche duplicazione. Ripuliamolo ora.

private bool isDivisibleBy(int divisor, int input) {
    return (input % divisor == 0);
}

public String FizzBuzz(int n) {
    if (isDivisibleBy(3, n))
        return "Fizz";
    if (isDivisibleBy(5, n))
        return "Buzz"
    return n.ToString();
}

Freddo. Ora abbiamo rimosso la duplicazione e creato una funzione ben denominata. Qual è il prossimo test che possiamo scrivere che ci costringerà a cambiare il codice? Bene, abbiamo evitato il caso in cui il numero è divisibile per 3 e 5. Scriviamolo ora.

Assert.Equal("FizzBuzz", FizzBuzz(15));

public String FizzBuzz(int n) {
    if (isDivisibleBy(3, n) && isDivisibleBy(5, n))
        return "FizzBuzz";
    if (isDivisibleBy(3, n))
        return "Fizz";
    if (isDivisibleBy(5, n))
        return "Buzz"
    return n.ToString();
}

I test superano, ma abbiamo più duplicazioni. Abbiamo opzioni, ma ho intenzione di applicare "Estrai variabile locale" alcune volte in modo che stiamo refactoring invece di riscrivere.

public String FizzBuzz(int n) {

    var isDivisibleBy3 = isDivisibleBy(3, n);
    var isDivisibleBy5 = isDivisibleBy(5, n);

    if ( isDivisibleBy3 && isDivisibleBy5 )
        return "FizzBuzz";
    if ( isDivisibleBy3 )
        return "Fizz";
    if ( isDivisibleBy5 )
        return "Buzz"
    return n.ToString();
}

E abbiamo coperto ogni input ragionevole, ma per quanto riguarda l' input irragionevole ? Cosa succede se passiamo 0 o un negativo? Scrivi quei casi di test.

public String FizzBuzz(int n) {

    if (n < 1)
        throw new InvalidArgException("n must be >= 1);

    var isDivisibleBy3 = isDivisibleBy(3, n);
    var isDivisibleBy5 = isDivisibleBy(5, n);

    if ( isDivisibleBy3 && isDivisibleBy5 )
        return "FizzBuzz";
    if ( isDivisibleBy3 )
        return "Fizz";
    if ( isDivisibleBy5 )
        return "Buzz"
    return n.ToString();
}

Sta iniziando a sembrare "codice reale" ancora? Ancora più importante, a che punto ha smesso di essere "codice irreale" e la transizione ad essere "reale"? È qualcosa su cui riflettere ...

Quindi, sono stato in grado di farlo semplicemente cercando un test che sapevo non avrebbe superato ad ogni passo, ma ho avuto molta pratica. Quando sono al lavoro, le cose non sono mai così semplici e potrei non sapere sempre quale test forzerà un cambiamento. A volte scrivo un test e rimango sorpreso nel vedere che già passa! Consiglio vivamente di prendere l'abitudine di creare un "Elenco di prova" prima di iniziare. Questo elenco di test dovrebbe contenere tutti gli input "interessanti" a cui puoi pensare. Potresti non usarli tutti e probabilmente aggiungerai casi mentre procedi, ma questo elenco funge da roadmap. La mia lista di test per FizzBuzz sarebbe simile a questa.

  • Negativo
  • Zero
  • Uno
  • Due
  • Tre
  • quattro
  • Cinque
  • Sei (multiplo non banale di 3)
  • Nove (3 quadrati)
  • Dieci (multiplo non banale di 5)
  • 15 (multipli di 3 e 5)
  • 30 (multiplo non banale di 3 e 5)

3
I commenti non sono per una discussione estesa; questa conversazione è stata spostata in chat .
maple_shaft

47
A meno che non fraintenda completamente questa risposta: "Potremmo fare qualcosa di stupido come se n == 1, ma salteremo alla soluzione sana". - l'intera cosa era sciocca. Se conosci in anticipo vuoi una funzione che faccia <spec>, scrivi i test per <spec> e salta la parte in cui scrivi le versioni che ovviamente falliscono <spec>. Se trovi un bug in <spec>, assicurati: scrivi prima un test per verificare che puoi esercitarlo prima della correzione e osserva i passaggi del test dopo la correzione. Ma non è necessario falsificare tutti questi passaggi intermedi.
GManNickG,

16
I commenti che evidenziano i principali difetti di questa risposta e TDD in generale sono stati spostati in chat. Se stai pensando di utilizzare TDD, leggi la 'chat'. Purtroppo i commenti di "qualità" sono ora nascosti in un carico di chat che i futuri studenti possono leggere.
user3791372,

2
@GManNickG Credo che il punto sia ottenere la giusta quantità di test. Scrivere i test in anticipo rende facile perdere quali casi speciali dovrebbero essere testati, portando a situazioni che non sono adeguatamente coperte nei test, o essenzialmente alla stessa situazione che è inutilmente coperta un sacco di volte nei test. Se riesci a farlo senza questi passaggi intermedi, fantastico! Tuttavia, non tutti possono farlo, è qualcosa che richiede pratica.
hvd,

1
Ed ecco una citazione di Kent Beck sul refactoring: "Ora che il test viene eseguito, possiamo realizzare (come in" rendere reale ") l'implementazione del sommario ()". Quindi procede a cambiare una costante in una variabile. Ho sentito che questa citazione corrispondeva abbastanza bene alla domanda.
Chris Wohlert il

46

Il codice "reale" è il codice che scrivi per far passare il test. Davvero . È così semplice.

Quando le persone parlano di scrivere il minimo indispensabile per rendere il test verde, ciò significa che il tuo vero codice dovrebbe seguire il principio YAGNI .

L'idea del passaggio del refactor è solo quella di ripulire ciò che hai scritto una volta che sei soddisfatto che soddisfi i requisiti.

Fintanto che i test scritti in realtà comprendono i requisiti del prodotto, una volta superati, il codice è completo. Pensaci, se tutti i tuoi requisiti aziendali hanno un test e tutti questi test sono verdi, cosa c'è di più da scrivere? (Va bene, nella vita reale non tendiamo ad avere una copertura completa del test, ma la teoria è solida.)


45
I test unitari non possono effettivamente comprendere i requisiti del prodotto anche per requisiti relativamente banali. Nella migliore delle ipotesi, campionano lo spazio input-output e l'idea è di generalizzare (correttamente) l'intero spazio input-output. Naturalmente, il tuo codice potrebbe essere un grosso switchproblema con un caso per ogni unit test che supererebbe tutti i test e fallirebbe per qualsiasi altro input.
Derek Elkins,

8
@DerekElkins TDD obbliga a fallire i test. Test unitari non falliti.
Taemyr,

6
@DerekElkins è per questo che non scrivi solo unit test, e anche perché c'è un presupposto generale che stai provando a fare qualcosa, non solo a fingere!
jonrsharpe,

36
@jonrsharpe Con questa logica, non scriverei mai implementazioni banali. Ad esempio nell'esempio di FizzBuzz nella risposta di RubberDuck (che utilizza solo test unitari), la prima implementazione chiaramente "finge semplicemente". La mia comprensione della domanda è esattamente questa dicotomia tra la scrittura di codice che sai essere incompleta e il codice che credi sinceramente implementerà il requisito, il "codice reale". Il mio "grande switch" era inteso come un estremo logico di "scrivere il minimo indispensabile per rendere i test verdi". Vedo la domanda del PO come: dove in TDD è il principio che evita così grande switch?
Derek Elkins,

2
@GenericJon È un po 'troppo ottimista nella mia esperienza :) Per prima cosa, ci sono persone a cui piace lavorare ripetutamente senza cervello. Saranno più felici con una gigantesca dichiarazione switch che con un "processo decisionale complicato". E per perdere il lavoro, o avrebbero bisogno di qualcuno che li chiami sulla tecnica (e meglio avere buone prove che sta effettivamente perdendo le opportunità / i soldi dell'azienda!), O fare eccezionalmente male. Dopo aver assunto la manutenzione di molti di questi progetti, posso dire che è facile per un codice molto ingenuo durare per decenni, purché renda felice (e pagando) il cliente.
Luaan,

14

La risposta breve è che il "codice reale" è il codice che fa passare il test. Se riesci a superare il test con qualcosa di diverso dal codice reale, aggiungi altri test!

Concordo sul fatto che molti tutorial su TDD sono semplicistici. Funziona contro di loro. Un test troppo semplice per un metodo che, per esempio, calcola 3 + 8 non ha davvero altra scelta che calcolare anche 3 + 8 e confrontare il risultato. Ciò fa sembrare che dovrai solo duplicare il codice dappertutto e che il test è inutile e soggetto a errori.

Quando sei bravo nel test, ciò ti informerà su come strutturi la tua applicazione e su come scrivi il tuo codice. Se hai difficoltà a trovare test utili e sensati, probabilmente dovresti ripensare un po 'il tuo progetto. Un sistema ben progettato è facile da testare, il che significa che i test sensibili sono facili da pensare e da implementare.

Quando scrivi prima i tuoi test, guardali fallire e poi scrivi il codice che li fa passare, questa è una disciplina per garantire che tutto il tuo codice abbia test corrispondenti. Non seguo scrupolosamente questa regola quando sto programmando; spesso scrivo test dopo il fatto. Ma fare i test prima aiuta a mantenerti onesto. Con una certa esperienza, inizierai a notare quando ti stai codificando in un angolo, anche quando non scrivi prima i test.


6
Personalmente, il test che scrivo sarebbe assertEqual(plus(3,8), 11), no assertEqual(plus(3,8), my_test_implementation_of_addition(3,8)). Per casi più complessi, si cerca sempre un modo per dimostrare il risultato corretto, oltre a calcolare dinamicamente il risultato corretto nel test e verificare l'uguaglianza.
Steve Jessop,

Quindi, per un modo davvero sciocco di farlo per questo esempio, potresti provare che plus(3,8)ha restituito il risultato corretto sottraendo 3 da esso, sottraendo 8 da quello e controllando il risultato contro 0. Questo è ovviamente equivalente assertEqual(plus(3,8), 3+8)a essere un un po 'assurdo, ma se il codice in esame sta costruendo qualcosa di più complicato di un semplice numero intero, quindi prendere il risultato e verificare la correttezza di ogni parte è spesso l'approccio giusto. In alternativa, qualcosa del generefor (i=0, j=10; i < 10; ++i, ++j) assertEqual(plus(i, 10), j)
Steve Jessop,

... poiché ciò evita la grande paura, che è che quando scriviamo il test faremo lo stesso errore in materia di "come aggiungere 10" che abbiamo fatto nel codice live. Quindi il test evita accuratamente di scrivere qualsiasi codice che aggiunge 10 a qualsiasi cosa, nel test che plus()può aggiungere 10 a cose. Facciamo ancora affidamento sui valori del ciclo iniziale verificato dal programmatore, ovviamente.
Steve Jessop,

4
Voglio solo sottolineare che anche se stai scrivendo dei test dopo il fatto, è comunque una buona idea vederli fallire; trova una parte del codice che sembra cruciale per qualunque cosa tu stia lavorando, modificalo un po '(es. sostituisci un + con un - o qualsiasi altra cosa), esegui i test e guardali fallire, annulla la modifica e guardali passare. Molte volte l'ho fatto in realtà il test non fallisce, rendendolo peggio che inutile: non solo non sta testando nulla, mi sta dando la falsa fiducia che qualcosa sia in fase di test!
Warbo,

6

A volte alcuni esempi di TDD possono essere fuorvianti. Come altre persone hanno già sottolineato in precedenza, il codice che scrivi per far passare i test è il vero codice.

Ma non pensare che il vero codice appaia magico - è sbagliato. Hai bisogno di una migliore comprensione di ciò che vuoi ottenere e quindi devi scegliere il test di conseguenza, a partire dai casi più semplici e dai casi angolari.

Ad esempio, se hai bisogno di scrivere un lexer, inizi con una stringa vuota, quindi con un mucchio di spazi bianchi, quindi un numero, quindi con un numero circondato da spazi bianchi, quindi un numero sbagliato, ecc. Queste piccole trasformazioni ti porteranno a l'algoritmo giusto, ma non si passa dal caso più semplice a un caso molto complesso scelto in modo stupido per ottenere il vero codice.

Bob Martin lo spiega perfettamente qui .


5

La parte del refactor viene ripulita quando sei stanco e vuoi andare a casa.

Quando stai per aggiungere una funzione, la parte del refactor è ciò che cambi prima del prossimo test. Rifattrai il codice per fare spazio alla nuova funzionalità. Lo fai quando sai quale sarà quella nuova funzionalità. Non quando lo stai solo immaginando.

Questo può essere semplice come rinominare GreetImplper GreetWorldprima di creare una GreetMomclasse (dopo l'aggiunta di un test) per aggiungere una funzionalità che stamperà "Hi Mom".


1

Ma il vero codice apparirebbe nella fase del refactor della fase TDD. Vale a dire il codice che dovrebbe far parte della versione finale.

I test devono essere eseguiti ogni volta che si effettua una modifica.

Il motto del ciclo di vita del TDD sarebbe: REFATTORE VERDE ROSSO

ROSSO : scrivi i test

VERDE : fai un onesto tentativo di ottenere il codice funzionale che superi i test il più rapidamente possibile: codice duplicato, hack di variabili di nome oscuro di altissimo livello, ecc.

RIFATTORE : ripulire il codice, denominare correttamente le variabili. ASCIUGARE il codice.


6
So cosa stai dicendo sulla fase "Verde", ma implica che i valori di ritorno del cablaggio fisso per superare i test potrebbero essere appropriati. Nella mia esperienza, "Green" dovrebbe essere un onesto tentativo di fare in modo che il codice di lavoro soddisfi i requisiti, potrebbe non essere perfetto ma dovrebbe essere completo e "spedibile" come lo sviluppatore può gestire in un primo passaggio. Il refactoring è probabilmente preferibile qualche tempo dopo aver fatto più sviluppo e i problemi con il primo passaggio diventano più evidenti ed emergono opportunità di DRY.
mcottle,

2
@mcottle: potresti essere sorpreso di quante implementazioni di un repository get-only possono essere valori hardcoded nella base di codice. :)
Bryan Boettcher,

6
Perché dovrei mai scrivere codice merda e ripulirlo, quando riesco a tirare fuori un bel codice di qualità di produzione quasi più veloce che posso scrivere? :)
Kaz,

1
@Kaz Perché in questo modo rischi di aggiungere un comportamento non testato . L'unico modo per assicurarsi di avere un test per ogni comportamento desiderato è quello di fare il possibile cambiamento semplice indipendentemente da quanto sia scadente. A volte il seguente refactoring fa apparire un nuovo approccio a cui non avevi pensato in anticipo ...
Timothy Truckle,

1
@TimothyTruckle Cosa succede se ci vogliono 50 minuti per trovare la modifica più semplice possibile, ma solo 5 per trovare la seconda modifica più semplice possibile? Andiamo con il secondo più semplice o continuiamo a cercare il più semplice?
Kaz,

1

Quando scrivi il codice "reale" in TDD?

La fase rossa è dove scrivi il codice.

Nella fase di refactoring l'obiettivo primario è quello di eliminare il codice.

Nella fase rossa fai di tutto per rendere il test più veloce possibile e ad ogni costo . Ignori completamente ciò che hai mai sentito parlare di buone pratiche di codifica o di schemi di progettazione simili. Rendere il test verde è tutto ciò che conta.

Nella fase di refactoring pulisci il casino che hai appena fatto. Ora vedi per prima cosa se la modifica che hai appena apportato è il tipo più in alto nell'elenco delle priorità di trasformazione e se c'è qualche duplicazione di codice che puoi rimuovere molto probabilmente applicando un modello di design.

Infine, puoi migliorare la leggibilità rinominando gli identificatori ed estrai numeri magici e / o stringhe letterali alle costanti.


Non è un rifrattore rosso, è un rifrattore rosso-verde. - Rob Kinyon

Grazie per aver indicato questo.

Quindi è la fase verde in cui si scrive il codice reale

Nella fase rossa scrivi la specifica eseguibile ...


Non è un rifrattore rosso, è un rifrattore rosso-verde. Il "rosso" è prendere la tua suite di test dal verde (tutti i test passano) al rosso (un test fallisce). Il "verde" è dove prendi sciatto la tua suite di test dal rosso (un test fallisce) al verde (tutti i test superano). Il "refattore" è il punto in cui prendi il tuo codice e lo rendi grazioso mentre fai passare tutti i test.
Rob Kinyon,

1

Stai scrivendo Real Code per tutto il tempo.

Ad ogni passaggio Stai scrivendo un codice per soddisfare le condizioni che il tuo codice soddisferà per i futuri chiamanti del tuo codice (che potresti essere tu o no ...).

Pensi che non stai scrivendo un codice utile ( reale ), perché in un attimo potresti rifatturarlo.

Il refactoring del codice è il processo di ristrutturazione del codice del computer esistente, che modifica il factoring, senza modificarne il comportamento esterno.

Ciò significa che anche se si sta modificando il codice, le condizioni soddisfatte dal codice rimangono invariate. E i controlli ( test ) che hai implementato per verificare il tuo codice sono già lì per verificare se le tue modifiche hanno cambiato qualcosa. Quindi il codice che hai scritto tutto il tempo è lì, solo in un modo diverso.

Un'altra ragione per cui potresti pensare che non sia un vero codice, è che stai facendo degli esempi in cui il programma finale può già essere previsto da te. Questo è molto buono, poiché mostra che hai conoscenza del dominio in cui stai programmando.
Ma molte volte i programmatori si trovano in un dominio che è nuovo , a loro sconosciuto . Non sanno quale sarà il risultato finale e TDD è una tecnica per scrivere i programmi passo dopo passo, documentando le nostre conoscenze su come dovrebbe funzionare questo sistema e verificando che il nostro codice funzioni in questo modo.

Quando ho letto The Book (*) su TDD, per me la caratteristica più importante che è emersa è stata la lista: TODO. Mi ha dimostrato che TDD è anche una tecnica per aiutare gli sviluppatori a concentrarsi su una cosa alla volta. Quindi questa è anche una risposta alla tua domanda su Quanto codice reale scrivere ? Direi abbastanza codice per concentrarmi su 1 cosa alla volta.

(*) "Test Driven Development: By Example" di Kent Beck


2
"Test Driven Development: By Example" di Kent Beck
Robert Andrzejuk,

1

Non stai scrivendo codice per far fallire i tuoi test.

Scrivi i tuoi test per definire l'aspetto del successo, che inizialmente dovrebbe fallire perché non hai ancora scritto il codice che passerà.

L'intero punto sulla scrittura di test inizialmente falliti è fare due cose:

  1. Copri tutti i casi: tutti i casi nominali, tutti i casi limite, ecc.
  2. Convalida i tuoi test. Se li vedi mai passare, come puoi essere sicuro che riporteranno in modo affidabile un errore quando si verifica?

Il punto dietro il refactor rosso-verde è che scrivere prima i test corretti ti dà la sicurezza di sapere che il codice che hai scritto per superare i test è corretto e ti consente di refactoring con la sicurezza che i tuoi test ti informeranno non appena qualcosa si rompe, quindi puoi immediatamente tornare indietro e risolverlo.

Nella mia esperienza (C # /. NET), il test-first puro è un po 'un ideale irraggiungibile, perché non è possibile compilare una chiamata a un metodo che non esiste ancora. Quindi "test first" riguarda in realtà la codifica delle interfacce e l'implementazione di stub prima, quindi la scrittura di test contro gli stub (che inizialmente falliranno) fino a quando gli stub non saranno correttamente ripuliti. Non scriverò mai "codice in errore", ma solo costruendo da stub.


0

Penso che potresti essere confuso tra test unitari e test di integrazione. Credo che possano esserci anche test di accettazione, ma ciò dipende dal processo.

Dopo aver testato tutte le piccole "unità", le testerai tutte assemblate o "integrate". Di solito è un intero programma o libreria.

Nel codice che ho scritto l'integrazione verifica una libreria con vari programmi di test che leggono i dati e li inviano alla libreria, quindi controllano i risultati. Quindi lo faccio con i thread. Quindi lo faccio con discussioni e fork () nel mezzo. Quindi lo eseguo e uccido -9 dopo 2 secondi, quindi lo avvio e controllo la sua modalità di ripristino. Lo sfocato. Lo torturo in tutti i modi.

Tutto questo è ANCHE test, ma non ho un display abbastanza rosso / verde per i risultati. O ci riesce, oppure riesco a scavare tra qualche migliaio di righe di codice di errore per scoprire perché.

Ecco dove testate il "codice reale".

E ci ho appena pensato, ma forse non sai quando dovresti aver finito di scrivere unit test. Hai finito di scrivere unit test quando i tuoi test esercitano tutto ciò che hai specificato dovrebbe fare. A volte puoi perdere traccia di tutto ciò tra la gestione degli errori e i casi limite, quindi potresti voler creare un bel gruppo di test di test di percorso felici che passano semplicemente attraverso le specifiche.


(è = possessivo, è = "è" o "ha". Vedi ad esempio Come usare il suo e il suo .)
Peter Mortensen,

-6

In risposta al titolo della domanda: "Quando scrivi il codice" reale "in TDD?", La risposta è: "quasi mai" o "molto lentamente".

Sembri uno studente, quindi risponderò come se consigliassi uno studente.

Imparerai molte "teorie" e "tecniche" di programmazione. Sono perfetti per passare il tempo su corsi per studenti troppo cari, ma per te sono davvero pochi benefici che non potresti leggere in un libro in metà tempo.

Il compito di un programmatore è solo quello di produrre codice. Codice che funziona davvero bene. Ecco perché tu, il programmatore pianifica il codice nella tua mente, sulla carta, in un'applicazione adatta, ecc. E prevedi di aggirare in anticipo possibili difetti / lacune pensando logicamente e lateralmente prima di scrivere codice.

Ma devi sapere come rompere l'applicazione per poter progettare un codice decente. Ad esempio, se non sapessi di Little Bobby Table (xkcd 327), probabilmente non disinfetteresti i tuoi input prima di lavorare con il database, quindi non sarai in grado di proteggere i tuoi dati attorno a quel concetto.

TDD è solo un flusso di lavoro progettato per ridurre al minimo i bug nel codice creando i test di ciò che potrebbe andare storto prima di codificare l'applicazione perché la codifica può diventare esponenzialmente difficile più codice viene introdotto e si dimenticano i bug a cui si pensava una volta. Una volta che pensi di aver finito la tua applicazione, esegui i test e boom, speriamo che i bug vengano catturati con i tuoi test.

TDD non è - come alcuni credono - scrivere un test, farlo passare con un codice minimo, scrivere un altro test, farlo passare con un codice minimo, ecc. Invece, è un modo per aiutarti a programmare con sicurezza. Questo ideale del codice di refactoring continuo per farlo funzionare con i test è idiota, ma è un bel concetto tra gli studenti perché li fa sentire bene quando aggiungono una nuova funzionalità e stanno ancora imparando come programmare ...

Per favore, non cadere in questa trappola e vedere il tuo ruolo di codifica per quello che è - il compito di un programmatore è solo quello di produrre codice. Codice che funziona davvero bene. Ora, ricorda che sarai un programmatore professionista e che al tuo cliente non importa se hai scritto 100.000 asserzioni, o 0. Vogliono solo codice che funzioni. Davvero bene, in effetti.


3
Non sono nemmeno vicino a uno studente, ma leggo e cerco di applicare buone tecniche ed essere professionale. Quindi, in questo senso, sono uno "studente". Faccio solo domande basilari perché è così. Mi piace sapere esattamente perché sto facendo quello che sto facendo. Il nocciolo della questione. Se non lo capisco, non mi piace e inizio a fare domande. Devo sapere perché, se ho intenzione di usarlo. TDD sembra intuitivamente buono in alcuni modi come sapere cosa è necessario per creare e riflettere, ma l'implementazione è stata difficile da capire. Penso di avere una comprensione migliore ora.
johnny,


4
Queste sono le regole di TDD. Sei libero di scrivere il codice come vuoi, ma se non segui queste tre regole non stai facendo TDD.
Sean Burton,

2
"Regole" di una persona? TDD è un suggerimento per aiutarti a programmare, non una religione. È triste vedere così tante persone aderire a un'idea così analmente. Anche l'origine del TDD è controversa.
user3791372,

2
@ user3791372 TDD è un processo molto rigoroso e chiaramente definito. Anche se molti pensano che significhi solo "Fai dei test quando stai programmando", non lo è. Proviamo a non confondere i termini qui, questa domanda riguarda il processo TDD, non i test generali.
Alex,
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.