Quali sono gli svantaggi della programmazione test-first?


47

Oggi è di gran moda. "Tutti" lo consiglia. Questo di per sé mi rende sospettoso.

Quali sono alcuni svantaggi che hai riscontrato durante lo sviluppo test-first (test-driven)? Sto cercando esperienze personali da professionisti esperti - posso leggere le ipotetiche riflessioni di un centinaio di aspiranti altrove su Internet.

Non chiedo perché sto cercando di odiare il TDD, ma perché il mio compito è migliorare il processo di sviluppo del software e più possiamo conoscere i problemi che le persone incontrano, maggiori sono le possibilità che abbiamo di migliorare il processo.

Risposte:


41

Ce ne sono parecchi, ma i vantaggi superano di gran lunga gli svantaggi.

C'è una ripida curva di apprendimento.

Molti sviluppatori sembrano aspettarsi di poter essere efficienti con la programmazione test-first fin dal primo giorno. Purtroppo ci vuole molto tempo per acquisire esperienza e programmare alla stessa velocità di prima. Non puoi aggirarlo.

Per essere più specifici, è molto facile sbagliarsi. Puoi facilmente (con ottime intenzioni) finire per scrivere un sacco di test che sono difficili da mantenere o testare cose sbagliate. È difficile fornire esempi qui: questo tipo di problemi richiede semplicemente esperienza per risolverli. Devi avere una buona idea di separare le preoccupazioni e progettare per la testabilità. Il mio miglior consiglio qui sarebbe di programmare la coppia con qualcuno che conosca davvero bene TDD.

Fai più codice all'inizio.

Test-first significa che non puoi saltare i test (il che è buono) e significa che finirai per scrivere più codice in anticipo. Questo significa più tempo. Ancora una volta, non puoi aggirarlo. Vieni premiato con un codice che è più facile da mantenere, estendere e generalmente meno bug, ma richiede tempo.

Può essere una vendita difficile per i manager.

I gestori di software si occupano generalmente solo delle scadenze. Se passi alla programmazione test-first e all'improvviso stai impiegando 2 settimane per completare una funzione anziché una, non piacerà. Questa è sicuramente una battaglia che vale la pena combattere e molti manager sono abbastanza illuminati da ottenerla, ma può essere una vendita difficile.

Può essere una vendita difficile per i colleghi sviluppatori.

Dal momento che c'è una ripida curva di apprendimento, non tutti gli sviluppatori amano la programmazione test-first. In effetti, immagino che all'inizio alla maggior parte degli sviluppatori non piaccia. Puoi fare cose come la programmazione in coppia per aiutarli ad accelerare, ma può essere una vendita difficile.

Alla fine, i vantaggi superano gli svantaggi, ma non aiuta se si ignorano solo gli svantaggi. Sapere con cosa hai a che fare fin dall'inizio ti aiuta a negoziare alcuni, se non tutti, gli svantaggi.


Queste sono buone risposte, ma potrebbero essere più specifiche su # 1? Sono particolarmente interessato a sapere come / se sei riuscito a recuperare la tua velocità di programmazione - cosa hai imparato che non sapevi quando hai iniziato a fare TDD?
Alex Feinman,

Aggiornato per dare qualche chiarimento
Jaco Pretorius,

7
Se stai eseguendo dei test ora, il tempo totale impiegato nello sviluppo non dovrebbe cambiare in modo significativo. Sembra solo che le cose stiano impiegando più tempo perché stai esponendo il tempo necessario per scrivere e mantenere i test unitari.
ChrisF

1
@JeffO hai familiarità con "Sto per scrivere un minivan!" scuola di programmazione?
Alex Feinman,

1
@tvanfosson - perché stanno provando a cambiare due cose contemporaneamente - sia iniziando i test che TDD - che può essere problematico. Aggiunge anche alle stime dei tempi - abbastanza correttamente - quindi i manager e i clienti vedono solo l'aumento iniziale, non che il tempo complessivo sia effettivamente noto (per una volta) e potrebbe anche essere inferiore. Se stanno facendo dei test, questo aumento sarà inferiore.
ChrisF

35

Test-first presuppone che tu stia scrivendo un codice che è:

  • testabile in modo unit-test
  • che quello che stai sviluppando ha un approccio ovvio e non richiederà prototipi o sperimentazioni approfondite
  • che non sarà necessario eseguire il refactoring eccessivo o che si ha il tempo di riscrivere ripetutamente centinaia o migliaia di casi di test
  • nulla è sigillato
  • tutto è modulare
  • tutto è iniettabile o beffardo
  • che la tua organizzazione attribuisce un valore sufficientemente elevato ai difetti bassi per giustificare il sink di risorse
  • che c'è qualcosa di utile da testare a livello di unit test

Se il tuo progetto non soddisfa tali requisiti, avrai difficoltà. I promotori di TDD non hanno buone risposte a questo altro per suggerire di ridisegnare il prodotto per rientrare al meglio in quelle linee. Ci sono situazioni in cui ciò è impossibile o indesiderabile.

In pratica, posso anche riscontrare un grosso problema con le persone che pensano che i test test-first dimostrino effettivamente qualcosa sulla corretta funzione del programma. In molti casi questo non è vero, ma anche nei casi in cui è vero è ben lungi dall'essere un quadro completo della correttezza. Le persone vedono centinaia di test superati e presumono che sia sicuro testare meno poiché prima del TDD hanno fatto comunque solo poche centinaia di casi di test. Nella mia esperienza TDD significa che devi avere ancora più test di integrazione poiché gli sviluppatori avranno anche la falsa sicurezza e il dolore di cambiare tutti i test per fare un grande redattore può portare gli sviluppatori a fare interessanti soluzioni.

Esempi:

Il mio miglior esempio personale è quando scrivo il codice di sicurezza per asp.net. Se sono pensati per funzionare in un ambiente ostile dalla configurazione della macchina, vengono scoperti, firmati e sigillati e, poiché stanno correndo contro oggetti divini IIS, sono molto difficili da deridere in modo corretto. Aggiungi alcuni vincoli per le prestazioni e l'uso della memoria e perdi molto rapidamente la flessibilità di utilizzare oggetti segnaposto nelle restanti aree.

Qualsiasi tipo di microcontrollore o altro codice di ambiente con risorse limitate potrebbe non essere possibile realizzare un vero design in stile OO poiché le astrazioni non si ottimizzano e si hanno limiti di risorse bassi. Lo stesso si può dire per le routine ad alte prestazioni anche in molti casi.


Puoi dare qualche controesempio? Quando non scriverei qualcosa che è testabile in modo unit-test? Perché non dovrei scrivere codice beffardo o iniettabile (diverso dal codice legacy, che è un argomento a sé stante)?
Alex Feinman,

modificato per aggiungere esempi di sezione
Bill

4
Concordato. Il lavoro TDD sembra basarsi su una serie di ipotesi sulle macchine con cui stai lavorando; non sembra vero per circa il 50% dei miei progetti.
Paul Nathan,

Sono totalmente d'accordo ... Ottima risposta
Khelben,

2
È come qualsiasi cosa in questo gioco - appropriato per molte situazioni, inappropriato per gli altri. Fai attenzione a chiunque sostenga un percorso vero in qualsiasi area dello sviluppo del software.
Alan B,

25

Il più grande svantaggio che ho visto non è con TDD stesso ma con i praticanti. Adottano un approccio dogmatico e zelante in cui tutto deve essere testato . A volte (molte volte), non è necessario. Inoltre, potrebbe non essere pratico (.ie. Introdurre un'organizzazione al TDD.)

Un buon ingegnere trova dei compromessi e applica il giusto equilibrio tra quando / dove / come applicare il test prima. Inoltre, se ti ritrovi a dedicare costantemente molto più tempo allo sviluppo di test anziché al codice effettivo (di un fattore 2-3 o più), sei nei guai.

In altre parole, sii pragmatico e ragionevole con TDD (o qualsiasi altra cosa nello sviluppo di software per quella materia).


È forse da lì che viene la "nuova" definizione di codice legacy di Michael Feathers (ovvero "codice senza test")?
Phill W.,

Questa definizione non funzionerebbe per me :) Per me, qualsiasi codice che viene eseguito in produzione e che è soggetto a modifiche è un codice legacy, indipendentemente dal codice o dalla qualità del test. In genere associamo "codice legacy" a "codice errato" o "codice obsoleto" quando in realtà codice errato e codice obsoleto sono già presenti nel codice in fase di sviluppo che non ha ancora visto l'utilizzo della produzione. Il nostro obiettivo dovrebbe essere che il nostro codice sia legacy sin dall'inizio, e che sia di tale qualità e utilità che rimanga in uso per anni, decenni a venire.
luis.espinal,

6

Ho iniziato a fare TDD all'inizio di agosto 2009 e ho convinto tutta la mia compagnia a passare ad essa a settembre / ottobre 2009. Attualmente, l'intero team di sviluppo è completamente convertito e commettere codice non testato nel repository è considerato una cosa negativa e viene lanciato. Ha funzionato benissimo per noi e non riesco a immaginare di tornare al codice dei cowboy.

Tuttavia, ci sono due problemi che sono piuttosto evidenti.

La suite di test deve essere mantenuta

Quando prendi sul serio TDD, finirai per scrivere molti test. Inoltre, ci vuole un po 'di tempo ed esperienza per rendersi conto di quale sia la giusta granularità dei test (esagerare è quasi altrettanto grave che esagerare). Questi test sono anche codice e sono suscettibili di bitrot. Ciò significa che devi mantenerli come tutto il resto: aggiornalo quando aggiorni le librerie da cui dipendono, fai il refactor di volta in volta ... Quando apporti grandi modifiche al tuo codice, molti test diventeranno improvvisamente obsoleti o anche chiaramente sbagliato. Se sei fortunato, puoi semplicemente cancellarli, ma molte volte finirai per estrarre i bit utili e adattarli alla nuova architettura.

Test di astrazioni perdono di volta in volta

Stiamo usando Django, che ha un framework di test davvero eccezionale. Tuttavia, a volte fa ipotesi che sono leggermente in contrasto con la realtà. Ad esempio, alcuni middleware potrebbero interrompere i test. Oppure, alcuni test fanno ipotesi su un backend di cache. Inoltre, se si utilizza un db "reale" (non SQLite3), la preparazione del db per i test richiederà molto tempo. Certo, puoi (e dovresti) usare SQLite3 e un db in memoria per i test che esegui localmente, ma alcuni codici si comporteranno in modo diverso a seconda del database che utilizzi. La configurazione di un server di integrazione continua che viene eseguita in una configurazione realistica è un must.

(Alcune persone ti diranno che dovresti prendere in giro tutte le cose come il database, o i tuoi test non sono "puri", ma è solo ideologia. Se commetti errori nel tuo codice beffardo (e credimi, lo farai), la tua suite di test sarà inutile.)

Detto questo, i problemi che ho descritto iniziano ad essere evidenti solo quando sei abbastanza avanzato con TDD ... Quando inizi con TDD (o lavori su progetti più piccoli) il refactoring dei test non sarà un problema.


3
+1. "deve essere mantenuto": questo è molto meno un problema quando si verifica il codice riutilizzabile, poiché la sua interfaccia e il suo comportamento devono normalmente essere stabili. Per questo motivo, normalmente faccio TDD solo per la nostra libreria riutilizzabile.
Dimitri C.,

4

Per me c'è qualche profondo problema psicologico con i test ogni volta che provo ad applicarli ampiamente, come in TDD: se sono lì, scrivo in modo sciatto perché confido che i test colmino eventuali problemi. Ma se non ci sono test per fornire una rete di sicurezza, scrivo attentamente il codice e il risultato è invariabilmente migliore rispetto ai test.

Forse sono solo io. Ma ho anche letto da qualche parte che le auto con tutti i tipi di campane e fischietti di sicurezza tendono a schiantarsi di più (perché i conducenti sanno che le caratteristiche di sicurezza sono lì), quindi forse questo è qualcosa da riconoscere; TDD può essere incompatibile con alcuni individui.


Mi sembra strano, dato che scrivere codice testabile di solito mi fa rallentare e pensare di più a ciò che sto programmando. Al giorno d'oggi ho un po 'di codice nervoso senza test.
Matt H

1
Mostra solo che persone diverse reagiscono davvero diversamente. Non sto colpendo TDD - ovviamente molte persone lo trovano utile - ma il fatto è che non è per tutti.
Joonas Pulakka,

2
Sono d'accordo al 100%. Scrivo il codice meglio e più velocemente senza test automatici. Certo sarebbe assurdo non testare, penso solo che l'automazione sia una cattiva scelta (almeno per me). Trovo che i test manuali siano entrambi più veloci rispetto al mantenimento di una suite di test e più sicuri, ma sono anche uno sviluppatore esperto, quindi sono molto bravo a sapere cosa testare e dove e perché, quindi le mie aggiunte di codice e i miei fattori secondari sono regressione gratuito.
Ben Lee,

1
Anche se dovrei sottolineare che il team con cui lavoro e i progetti sono entrambi abbastanza piccoli da avere un buon senso dell'intera architettura - su un team di grandi dimensioni o un progetto molto grande, potrei vedere i test automatizzati come più utili, perché quindi nessun singolo sviluppatore sarebbe necessariamente in grado di annusare dove dovrebbero essere testati per evitare regressioni.
Ben Lee,

Stai lasciando fuori il passaggio di refactoring?
rjnilsson,

2

Una situazione in cui test-first si mette davvero in mezzo è quando voglio provare rapidamente qualche idea e vedere se può funzionare prima di scrivere una corretta implementazione.

Il mio approccio è normalmente:

  1. Implementa qualcosa che funziona (proof of concept).
  2. Se funziona, consolidare aggiungendo test, migliorando la progettazione, il refactoring.

A volte non riesco a passare al punto 2.

In questo caso, l'utilizzo di TDD si è rivelato avere più svantaggi che vantaggi per me:

  • Scrivere test durante l'implementazione della proof of concept mi ​​rallenta e interrompe il mio flusso di pensieri: voglio capire un'idea e non voglio perdere tempo a testare i dettagli della mia prima implementazione approssimativa.
  • Potrebbe volerci più tempo per scoprire se la mia idea vale qualcosa o no.
  • Se si scopre che l'idea era inutile, devo buttare via sia il mio codice che i miei test di unità ben scritti.

Quindi, quando devo esplorare alcune nuove idee, non uso TDD e introduco test unitari solo quando ho la sensazione che il nuovo codice stia arrivando da qualche parte.


1
Sembra che tu stia confondendo il codice prototipo con il codice utilizzabile. Il codice prototipo è un codice di prova . Non ha bisogno di essere testato e non è necessario creare test eseguiti su di esso. Il passaggio che ti manca è tra 1 e 2 .: dici "consolida scrivendo i test". Il problema è che non hai qualcosa da consolidare, ma qualcosa da scrivere. Pianifica di riscrivere il codice prototipo, non prevede di riutilizzarlo. Il riutilizzo lascia molto spazio al compromesso. La riscrittura formalizza la divisione tra la fase di esplorazione e la fase di "codice di qualità".
utnapistim,

3
@utnapistim: Non sto confondendo il codice prototipo con il codice utilizzabile, piuttosto, i fanatici TDD li confondono e suggeriscono che dovresti usare TDD anche per il codice prototipo. O meglio, presumono che non ci sia affatto un codice prototipo. Inoltre, sono d'accordo con te sul fatto che spesso devi riscrivere quando passi dal prototipo alla vera implementazione. A volte puoi riutilizzare parti del codice prototipo, ma devi essere pronto a riscrivere. Devi davvero decidere caso per caso.
Giorgio,

3
@utnapistim: vedi anche la risposta di luis.espinal: "Il più grande svantaggio che ho visto non è con il TDD stesso ma con i praticanti. Adottano un approccio dogmatico e zelante in cui tutto deve essere testato".
Giorgio,

1

Svantaggi o costi del TDD

Nota: esiste una gamma di diversi tipi di TDD. Indipendentemente da unità, BDD, ATDD o altre varianti rimangono molte difficoltà

Effetti collaterali

Che si tratti di derisione, dispositivi o test funzionali, le dipendenze da stati o sistemi esterni sono spesso la fonte della maggior complessità nei test, la confusione nel modo di testare e il rischio maggiore di sbagliarli. Alcuni problemi che ho visto:

  • Beffardo: dimentica di affermare l'ordine delle chiamate
  • Derisione: la derisione non corrisponde alla chiamata o alla risposta reale
  • Apparecchio: il test si basa su dati non realistici, mascherando altri problemi
  • Apparecchio: prova uno stato impossibile in produzione
  • Funzionale: false build si interrompe a causa della temporanea indisponibilità del sistema dipendente
  • Funzionale: la velocità del test è molto lenta

Dovrai cambiare il tuo approccio alla programmazione, per alcuni sarà un drastico cambiamento.

Diverse persone codificano in modi selvaggiamente diversi. In TDD è necessario iniziare con un test che asserisce un comportamento specifico e quindi implementare in modo che il test passi. Ho visto ed era un programmatore la cui programmazione non era favorevole a TDD. Mi ci sono voluti circa 2 mesi quando inizialmente ho iniziato ad abituarmi a cambiare il mio approccio di sviluppo.

Ci vuole tempo per capire cosa ti interessa dei test e cosa non ti interessa dei test.

Ogni squadra dovrebbe prendere una decisione esplicita su dove vuole tracciare la linea nei test. Quali cose apprezzano che vogliono testare e cosa no. Spesso è un processo doloroso imparare a scrivere buoni test e cosa ti interessa davvero dei test. Nel frattempo il codice continuerà ad essere in uno stato di flusso fino a quando non ci sarà coerenza sia nello stile che nell'approccio.

Specifico per unit test: grandi refactor

Un refactor grande o fondamentale di una base di codice significativa con decine di migliaia di unit test genererà un costo enorme per aggiornare tutti i test. Ciò si manifesterà spesso nel respingere il fare un refattore anche se è la cosa giusta da fare semplicemente perché i costi associati a farlo.


0

La mia analogia sono le barriere su una traccia Scalextric. Se li indossi, diventi molto meno cauto.

Le persone ottengono anche un po 'di spazio sui loro test - poiché funzionano bene, credono che il codice sia completamente testato mentre è solo l'inizio del processo di test.

Secondo me TDD è un trampolino di lancio per BDD. Una serie di test eseguiti non aiuta davvero gli sviluppatori senza sapere cosa fanno i test. Con BDD, l'output del test è in inglese che documenta il test e quindi migliora la comprensione del sistema.


-1

Il vantaggio di TDD è che ti costringe a proteggere il tuo codice da persone che non lo capiscono. Sì, questo spesso include te stesso. Ma cosa succede quando il codice non vale la pena proteggere? C'è un sacco di codice che non dovrebbe nemmeno essere lì in primo luogo! Quindi il problema con TDD è quando si tratta di sviluppatori che scrivono codice errato. TDD probabilmente non li aiuterà a scrivere un buon codice, è molto più probabile che scriveranno anche test orribili. Quindi nel loro caso TDD si aggiungerà solo al caos; test scritti male e / o ridondanti non sono più divertenti di altre forme di codice errato.


1
Se tu stesso non capisci il tuo codice, come può una manciata tra miliardi di possibili testcase proteggersi dal fatto che il codice sia sbagliato?
Michael Shaw,

2
Perché l'hai capito quando l'hai scritto ma te ne sei dimenticato lungo la strada?
Johan,

+1 TDD non protegge da uno sviluppatore che ha frainteso un requisito aziendale. È qui che entra in gioco BDD ...
Robbie Dee,
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.