Senso di test unitari senza TDD


28

Abbiamo un nuovo (piuttosto grande) progetto che stiamo pianificando di sviluppare usando TDD.

L'idea del TDD fallì (molte ragioni commerciali e non commerciali), ma in questo momento abbiamo una conversazione - dovremmo comunque scrivere unit test o no. Il mio amico dice che non c'è senso (o vicino allo zero) nella scrittura di unit test senza TDD, dovremmo concentrarci solo sui test di integrazione. Credo il contrario, che ha ancora senso scrivere testi unitari, solo per rendere il codice più a prova di futuro. Cosa pensi?

Aggiunto: Penso che questo non sia un duplicato di >> questa domanda << - Capisco la differenza tra UT e TDD. La mia domanda non riguarda le differenze , ma il senso di scrivere Test di unità senza TDD.


22
Sono curioso di
sapere

11
Scommetto che la stragrande maggioranza dei progetti con alcuni test unitari non utilizza TDD.
Casey,

2
Quali saranno i tuoi livelli di integrazione? Quali saranno le tue unità? Con quale frequenza eseguirai il refactoring al di sotto di ogni livello di test? Quanto saranno veloci i test di integrazione? Quanto sarà facile scrivere? Quanti casi combinatori generano parti diverse del codice? ecc ... Se non conosci le risposte a queste, forse è troppo presto per prendere decisioni ferme. A volte TDD è fantastico. A volte i benefici sono meno chiari. A volte i test unitari sono essenziali. A volte un discreto set di test di integrazione ti compra quasi altrettanto e sono molto più flessibili ... Tieni aperte le tue opzioni.
topo Ripristina Monica l'

2
Come alcuni consigli pratici per esperienza, non testare tutto se non stai facendo TDD. Prendi una decisione su quali tipi di test sono preziosi. Ho scoperto che i test unitari su metodi di input / output puri sono straordinariamente preziosi, mentre anche i test di integrazione a un livello molto alto dell'applicazione (ad esempio, l'invio di richieste Web su un'applicazione Web) sono straordinariamente preziosi. Fai attenzione ai test di integrazione di livello intermedio e ai test unitari che richiedono molta configurazione fittizia. Guarda anche questo video: youtube.com/watch?v=R9FOchgTtLM .
jpmc26

Il tuo aggiornamento non ha senso per quanto riguarda la domanda che hai posto. Se capisci la differenza tra TDD e Unit Test, cosa ti impedisce di scrivere test unitari. Votando per lasciare chiusa la tua domanda, anche se ho potuto vedere un argomento per la chiusura come "poco chiaro su quello che stai chiedendo" anziché duplicato.

Risposte:


52

Il TDD viene utilizzato principalmente (1) per garantire la copertura, (2) e per guidare la progettazione sostenibile, comprensibile e verificabile. Se non si utilizza TDD, non si ottiene la copertura del codice garantita. Ma ciò non significa in alcun modo che dovresti abbandonare quell'obiettivo e continuare a vivere beato con una copertura dello 0%.

I test di regressione sono stati inventati per un motivo. Il motivo è che, a lungo termine, ti fanno risparmiare più tempo in errori prevenuti rispetto allo sforzo aggiuntivo di scrivere. Questo è stato dimostrato più volte. Pertanto, a meno che tu non sia seriamente convinto che la tua organizzazione sia molto, molto meglio nell'ingegneria del software rispetto a tutti i guru che raccomandano il test di regressione (o se pensi di scendere molto presto in modo che non ci sia molto tempo per te), sì, tu dovrebbe assolutamente avere test unitari, esattamente per il motivo che si applica praticamente a qualsiasi altra organizzazione al mondo: perché rilevano errori prima dei test di integrazione e ciò ti farà risparmiare denaro. Non scriverli è come passare soldi gratis semplicemente stando in giro per strada.


12
"Se non si utilizza TDD, non si ottiene la copertura del codice garantita.": Non credo. Puoi sviluppare per due giorni e per i due giorni successivi scrivi i test. Il punto importante è che non consideri una funzionalità finita fino a quando non hai la copertura del codice desiderata.
Giorgio,

5
@DougM - In un mondo ideale forse ...
Telastyn,

7
Purtroppo TDD va di pari passo con il beffardo e fino a quando le persone smettono di farlo, tutto ciò dimostra che il test viene eseguito più velocemente . TDD è morto. Lunga vita ai test.
MickyD

17
TDD non garantisce la copertura del codice. È un presupposto pericoloso. È possibile codificare i test, superare tali test, ma avere ancora casi limite.
Robert Harvey,

4
@MickyDuncan Non sono del tutto sicuro di aver compreso appieno la tua preoccupazione. Il derisione è una tecnica perfettamente valida utilizzata per isolare un componente dagli altri in modo che i test del comportamento di quel componente possano essere eseguiti in modo indipendente. Sì, portato all'estremo può portare a software troppo ingegnerizzato, ma anche a qualsiasi tecnica di sviluppo se utilizzata in modo inappropriato. Inoltre, come afferma DHH nell'articolo che citi, l'idea di utilizzare sempre e soltanto i test di sistema completi è altrettanto negativa, se non addirittura peggiore. È importante usare il giudizio per decidere quale sia il modo migliore di testare una particolare caratteristica .
Jules il

21

Ho un aneddoto rilevante da qualcosa che sta accadendo proprio ora per me. Sono su un progetto che non utilizza TDD. I nostri addetti al controllo qualità ci stanno spingendo in quella direzione, ma siamo un piccolo vestito ed è stato un processo lungo e complesso.

Comunque , di recente stavo usando una libreria di terze parti per svolgere un'attività specifica. Si è verificato un problema relativo all'uso di quella libreria, quindi mi è stato chiesto di scrivere essenzialmente una versione della stessa libreria per conto mio. In totale, alla fine sono state circa 5.000 righe di codice eseguibile e circa 2 mesi del mio tempo. So che le righe di codice sono una metrica scadente, ma per questa risposta ritengo che sia un discreto indicatore di grandezza.

C'era una particolare struttura di dati di cui avevo bisogno che mi avrebbe permesso di tenere traccia di un numero arbitrario di bit. Dato che il progetto è in Java, ho scelto Java BitSete l'ho modificato un po '(avevo bisogno della capacità di tracciare anche i primi 0, cosa che BitSet di Java non fa per qualche motivo .....). Dopo aver raggiunto una copertura del 93% circa, ho iniziato a scrivere alcuni test che avrebbero effettivamente sottolineato il sistema che avevo scritto. Avevo bisogno di confrontare alcuni aspetti della funzionalità per assicurarmi che fossero abbastanza veloci per i miei requisiti finali. Non sorprende che una delle funzioni che avevo ignorato BitSetdall'interfaccia fosse assurdamente lenta quando si trattava di set di bit di grandi dimensioni (in questo caso centinaia di milioni di bit). Altre funzioni sostituite si basavano su questa funzione, quindi era un enorme collo di bottiglia.

Quello che ho finito per andare è stato andare al tavolo da disegno e capire un modo per manipolare la struttura sottostante di BitSet, che è un long[]. Ho progettato l'algoritmo, chiesto ai colleghi il loro contributo e poi ho iniziato a scrivere il codice. Quindi, ho eseguito i test unitari. Alcuni si sono rotti e quelli che mi hanno indicato esattamente dove dovevo cercare nel mio algoritmo per risolverlo. Dopo aver corretto tutti gli errori dai test unitari, sono stato in grado di dire che la funzione funziona come dovrebbe. Per lo meno, potrei essere altrettanto sicuro che questo nuovo algoritmo ha funzionato così come l'algoritmo precedente.

Naturalmente, questo non è a prova di proiettile. Se c'è un bug nel mio codice che i test unitari non stanno verificando, allora non lo saprò. Ma, naturalmente, quello stesso esatto bug avrebbe potuto essere presente anche nel mio algoritmo più lento. Tuttavia , posso dire con un certo grado di fiducia che non devo preoccuparmi dell'output errato di quella particolare funzione. I test unitari preesistenti mi hanno risparmiato ore, forse giorni, di provare a testare il nuovo algoritmo per assicurarmi che fosse corretto.

Questo è il punto di avere test unitari indipendentemente dal TDD - vale a dire, i test unitari lo faranno per te in TDD e al di fuori di TDD, quando finirai per refactoring / mantenimento del codice. Naturalmente, questo dovrebbe essere accoppiato con test di regressione regolari, test del fumo, test fuzzy, ecc., Ma i test unitari , come dice il nome, testano le cose sul livello atomico più piccolo possibile, che ti dà la direzione su dove sono comparsi gli errori.

Nel mio caso, senza i test unitari esistenti, dovrei in qualche modo trovare un metodo per garantire che l'algoritmo funzioni sempre. Che, alla fine ... sembra molto simile ai test unitari , non è vero?


7

Puoi suddividere il codice approssimativamente in 4 categorie:

  1. Cambiamenti semplici e raramente.
  2. Modifiche semplici e frequenti.
  3. Modifiche complesse e raramente.
  4. Modifiche complesse e frequenti.

I test unitari diventano più preziosi (probabilmente catturano bug importanti) più in basso nell'elenco. Nei miei progetti personali, faccio quasi sempre TDD sulla categoria 4. Nella categoria 3 di solito faccio TDD a meno che il test manuale non sia più semplice e veloce. Ad esempio, il codice antialiasing sarebbe complesso da scrivere, ma molto più facile da verificare visivamente rispetto alla scrittura di un unit test, quindi il test unitario varrebbe la pena per me solo se quel codice cambiava frequentemente. Il resto del mio codice lo metto sotto test unitario solo dopo aver trovato un bug in quella funzione.

A volte è difficile sapere in anticipo in quale categoria rientra un determinato blocco di codice. Il valore di TDD è che non si perde accidentalmente nessuno dei complessi test unitari. Il costo del TDD è tutto il tempo impiegato a scrivere i semplici test unitari. Tuttavia, di solito le persone con esperienza in un progetto sanno con ragionevole certezza in quale categoria rientrano le diverse parti del codice. Se non stai facendo TDD, dovresti almeno provare a scrivere i test più preziosi.


1
Quando lavoro su codice come suggerisci con il tuo esempio di antialiasing, trovo che la cosa migliore sia sviluppare il codice sperimentalmente, quindi aggiungere alcuni test di caratterizzazione per garantire che non rompa accidentalmente l'algoritmo in seguito. I test di caratterizzazione sono molto rapidi e facili da sviluppare, quindi il sovraccarico di farlo è molto basso.
Jules il

1

Che si tratti di test di unità, componenti, integrazione o accettazione, la parte importante è che deve essere automatizzato. Non avere test automatici è un errore fatale per qualsiasi tipo di software, dai semplici CRUD ai calcoli più complessi. Il ragionamento è che la scrittura dei test automatici costerà sempre meno della continua necessità di eseguire tutti i test manualmente quando non lo fai, per ordini di grandezza. Dopo averli scritti, devi solo premere un pulsante per vedere se passano o falliscono. L'esecuzione dei test manuali richiederà sempre molto tempo e dipenderà dagli esseri umani (creature viventi che si annoiano, potrebbero non avere attenzione e così via) per essere in grado di verificare se i test hanno esito positivo o negativo. In breve, scrivi sempre test automatici.

Ora, sul motivo per cui il tuo collega potrebbe essere contrario a fare qualsiasi tipo di test unitario senza TDD: è probabilmente perché è più difficile fidarsi dei test scritti dopo il codice di produzione. E se non puoi fidarti dei tuoi test automatici, non valgono nulla . Dopo il ciclo TDD, è necessario innanzitutto eseguire un test non riuscito (per il motivo corretto) per poter scrivere il codice di produzione per farlo passare (e non più). Questo processo sta essenzialmente testando i tuoi test, quindi puoi fidarti di loro. Per non parlare del fatto che scrivere test prima del codice effettivo ti spinge a progettare le tue unità e i tuoi componenti per essere più facilmente testabili (alti livelli di disaccoppiamento, SRP applicato, ecc ...). Anche se, naturalmente, fare TDD richiede disciplina .

Invece, se si scrive prima tutto il codice di produzione, quando si scrivono i test per esso, ci si aspetta che passino alla prima esecuzione. Questo è molto problematico, perché potresti aver creato un test che copre il 100% del tuo codice di produzione, senza affermare il comportamento corretto (potrebbe anche non eseguire alcuna asserzione! L'ho visto accadere ), dal momento che non riesci a vederlo fallire per prima cosa controlla se fallisce per la giusta ragione. Pertanto, potresti avere falsi positivi. I falsi positivi alla fine romperanno la fiducia nella tua suite di test, costringendo essenzialmente le persone a ricorrere nuovamente ai test manuali, quindi avrai il costo di entrambi i processi (test di scrittura + test manuali).

Ciò significa che devi trovare un altro modo per testare i tuoi test , come fa TDD. Quindi ricorrere al debug, commentare parti del codice di produzione, ecc., Al fine di potersi fidare dei test. Il problema è che il processo di "test dei test" è molto più lento in questo modo. Aggiungendo questo tempo al tempo che passerai a eseguire manualmente test ad-hoc (perché non hai test automatici mentre stai codificando il codice di produzione), nella mia esperienza, si traduce in un processo complessivo che è molto più lento della pratica TDD "dal libro" (Kent Beck - TDD per esempio). Inoltre, sono disposto a fare una scommessa qui e dire che veramente "testare i tuoi test" dopo che sono stati scritti richiede molta più disciplina del TDD.

Quindi forse il tuo team può riconsiderare le "ragioni commerciali e non commerciali" per non fare TDD. Nella mia esperienza, le persone tendono a pensare che TDD sia più lento rispetto alla semplice scrittura di unit test dopo che il codice è stato fatto. Questa ipotesi è errata, come hai letto sopra.


0

Spesso la qualità dei test dipende dalla loro provenienza. Sono regolarmente colpevole di non fare TDD "reali" - scrivo un po 'di codice per dimostrare che la strategia che mi piacerebbe usare effettivamente funziona, quindi copro ogni caso che il codice dovrebbe supportare con i test in seguito. Di solito l'unità di codice è una classe, per darti un'idea generale di quanto lavoro farò felicemente senza copertura del test, vale a dire non molto. Ciò significa che il significato semantico dei test coincide perfettamente con il sistema in esame nel suo stato "finito", perché li ho scritti sapendo quali casi SUT soddisfa e in che modo li soddisfa.

Al contrario, TDD con la sua politica di refactoring aggressivo tende a test obsoleti almeno con la stessa velocità con cui è possibile scriverli come l'interfaccia pubblica del sistema in fase di test. Personalmente trovo che il carico di lavoro mentale sia nel progettare le unità funzionali dell'applicazione sia nel mantenere sincronizzata la semantica dei test che lo coprono per essere troppo elevato per mantenere la mia concentrazione e la manutenzione del test spesso scivola. La base di codice termina con dei test in sospeso che non testano nulla di valore o sono semplicemente sbagliati. Se hai la disciplina e la capacità mentale di mantenere aggiornata la suite di test, esercitati con TDD nel modo più rigoroso che desideri. No, quindi l'ho trovato meno efficace per questo motivo.


0

In realtà lo zio Bob ha menzionato un punto molto interessante in uno dei suoi video di Clean Coders. Ha detto che il ciclo Red-Green-Refactor può essere applicato in 2 modi.

Il primo è il modo TDD convenzionale. Scrivi un test fallito, quindi fai passare il test e infine refactor.

Il secondo modo è quello di scrivere un piccolo pezzo di codice di produzione e seguirlo immediatamente con il suo unit test e poi refactoring.

L'idea è di fare passi molto piccoli . Ovviamente perdi la verifica dal codice di produzione che il tuo test è passato dal rosso al verde, ma in alcuni casi in cui stavo lavorando principalmente con sviluppatori junior che si sono rifiutati anche di provare a capire TDD si è rivelato in qualche modo efficace.

Ancora una volta ripeto (e questo è stato sottolineato da Zio Bob) l'idea è di fare passi molto piccoli e testare immediatamente il pezzo di codice di produzione che è stato appena aggiunto.


"... l'idea è di fare passi molto piccoli e testare immediatamente il pezzo di codice di produzione che è stato appena aggiunto.": Non sono d'accordo. Cosa descrivi se è buono quando hai già una chiara idea di cosa vuoi fare e vuoi lavorare sui dettagli, ma devi prima ottenere il quadro generale. Altrimenti, andando in piccoli passi (test, sviluppo, test, sviluppo) ci si perde nei dettagli. "Se non sai dove stai andando potresti non arrivarci."
Giorgio,
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.