TDD vs. Produttività

129

Nel mio attuale progetto (un gioco, in C ++), ho deciso che avrei usato lo sviluppo Test Driven 100% durante lo sviluppo.

In termini di qualità del codice, è stato fantastico. Il mio codice non è mai stato così ben progettato o così privo di bug. Non mi vergogno quando guardo il codice che ho scritto un anno fa all'inizio del progetto, e ho acquisito un senso molto migliore su come strutturare le cose, non solo per essere più facilmente testabile, ma per essere più semplice da implementare e utilizzare .

Tuttavia ... è passato un anno da quando ho iniziato il progetto. Certo, posso lavorarci solo nel tempo libero, ma TDD mi sta ancora rallentando considerevolmente rispetto a quello a cui sono abituato. Ho letto che la più lenta velocità di sviluppo migliora nel tempo, e sicuramente penso a testare molto più facilmente di quanto facevo prima, ma ci sono stato per un anno e sto ancora lavorando a passo di lumaca.

Ogni volta che penso al prossimo passo che ha bisogno di lavoro, devo fermarmi ogni volta e pensare a come scrivere un test per questo, per permettermi di scrivere il codice vero e proprio. A volte rimango bloccato per ore, sapendo esattamente quale codice voglio scrivere, ma non sapendo come scomporlo abbastanza bene da coprirlo completamente con i test. Altre volte, penserò rapidamente a una dozzina di test e passerò un'ora a scrivere dei test per coprire un piccolo pezzo di codice reale che altrimenti richiederebbe alcuni minuti per scrivere.

Oppure, dopo aver terminato il 50 ° test per coprire una particolare entità nel gioco e tutti gli aspetti della sua creazione e utilizzo, guardo la mia lista delle cose da fare e vedo la prossima entità da codificare, e rabbrividisco per l'orrore pensato di scrivere altri 50 test simili per farlo implementare.

È arrivato al punto che, esaminando i progressi dell'ultimo anno, sto pensando di abbandonare il TDD per "finire il dannato progetto". Tuttavia, rinunciare alla qualità del codice che ne deriva non è qualcosa che non vedo l'ora di fare. Temo che se smetterò di scrivere test, allora uscirò dall'abitudine di rendere il codice così modulare e testabile.

Sto forse facendo qualcosa di sbagliato per essere ancora così lento a questo? Esistono alternative che acceleri la produttività senza perdere completamente i benefici? TAD? Meno copertura di prova? In che modo altre persone sopravvivono al TDD senza uccidere tutta la produttività e la motivazione?

    
posta Nairou 22.06.2011 - 03:55
fonte

10 risposte

75

Vorrei iniziare ringraziando per condividere la tua esperienza e esprimere le tue preoccupazioni ... che devo dire non sono rare.

  • Tempo / Produttività: La scrittura dei test è più lenta rispetto alla scrittura dei test. Se la consideri pertinente, sarei d'accordo. Tuttavia, se si esegue uno sforzo parallelo in cui si applica un approccio non TDD, è probabile che il tempo trascorso con il codice esistente di break-detect-debug-and-fix rimetterà il risultato negativo netto. Per me, TDD è il più veloce che posso fare senza compromettere la sicurezza del mio codice. Se trovi cose nel tuo metodo, che non aggiungono valore, eliminale.
  • Numero di test: se si codificano N cose, è necessario testare N cose. parafrasare una delle linee di Kent Beck " Verifica solo se vuoi che funzioni. "
  • Rimanere bloccato per ore: lo faccio anch'io (a volte e non più di > 20 minuti prima di interrompere la linea) .. È solo il tuo codice che ti dice che il design ha bisogno di un po 'di lavoro. Un test è solo un altro client per la tua classe SUT. Se un test riscontra difficoltà nell'utilizzo del tuo tipo, è probabile che lo saranno anche i tuoi clienti di produzione.
  • Test simili tedio: mi serve un contesto in più per scrivere una controargomentazione. Detto questo, fermati e pensa alla somiglianza. È possibile che i dati vengano sottoposti a tali test in qualche modo? È possibile scrivere test su un tipo di base? Quindi devi solo eseguire lo stesso set di test per ogni derivazione. Ascolta i tuoi test. Sii il tipo giusto di pigrone e vedi se riesci a trovare un modo per evitare il tedio.
  • Fermarsi a pensare a cosa devi fare dopo (il test / le specifiche) non è una brutta cosa. Al contrario, è consigliato per costruire "la cosa giusta". Di solito, se non riesco a pensare a come testarlo, di solito non riesco nemmeno a pensare all'implementazione. È una buona idea svuotare le idee di implementazione fino a quando non ci arrivi .. forse una soluzione più semplice è oscurata da un disegno preventivo di YAGNI.

E questo mi porta alla domanda finale: come posso migliorare? La mia risposta (o An) è Leggi, riflette e pratica.

es. Di recente, tengo d'occhio

  • se il mio ritmo riflette RG [Rif] RG [Rif] RG [Rif] o è RRRRGRRef.
  • % di tempo trascorso nello stato Errore Red / Compile.
  • Sono bloccato in uno stato di build Red / Broken?
risposta data 22.06.2011 - 07:09
fonte
31

Non è necessario il 100% della copertura del test. Sii pragmatico.

    
risposta data 24.06.2011 - 02:48
fonte
22

TDD is still slowing me down considerably

Questo è in realtà falso.

Senza TDD, trascorri alcune settimane scrivendo il codice che funziona principalmente e trascorri l'anno successivo "testando" e correggendo molti (ma non tutti) i bug.

Con TDD, trascorri un anno a scrivere un codice che funziona davvero. Quindi esegui i test di integrazione finali per alcune settimane.

Il tempo trascorso probabilmente sarà lo stesso. Il software TDD sarà di qualità sostanzialmente migliore.

    
risposta data 22.06.2011 - 04:01
fonte
20

Or, after finishing the 50th test to cover a particular entity in the game and all aspects of it's creation and usage, I look at my to-do list and see the next entity to be coded, and cringe in horror at the thought of writing another 50 similar tests to get it implemented.

Questo mi fa chiedere quanto stai seguendo il passaggio "Refactor" di TDD.

Quando passano tutti i test, è giunto il momento per te di rifattorizzare il codice e rimuovere la duplicazione. Mentre la gente di solito lo ricorda, a volte dimentica che questo è anche il momento di refactoring test , per rimuovere la duplicazione e semplificare le cose.

Se hai due entità che si fondono in una per abilitare il riutilizzo del codice, prendi in considerazione di unire anche i loro test. Devi solo testare differenze incrementali nel tuo codice. Se non esegui regolarmente la manutenzione dei test, questi possono diventare rapidamente ingombranti.

Un paio di punti filosofici su TDD che potrebbero essere utili:

  • Quando non riesci a capire come scrivere un test, nonostante la vasta esperienza di scrittura dei test, allora questo è sicuramente un odore di codice . Il tuo codice è in qualche modo privo di modularità, il che rende difficile la scrittura di test piccoli e semplici.
  • Spiking di un po 'di codice è perfettamente accettabile quando usi TDD. Scrivi il codice che vuoi, per avere un'idea di come appare, quindi elimina il codice e inizia con i test.
  • Vedo praticare il TDD estremamente rigido come forma di esercizio. Al primo avvio, scrivere definitivamente un test prima ogni volta e scrivere il codice più semplice per eseguire il test prima di procedere. Tuttavia, questo non è necessario una volta diventato più comodo con la pratica. Non ho un test unitario per ogni singolo percorso di codice possibile che scrivo, ma attraverso l'esperienza sono in grado di scegliere ciò che deve essere testato con un test unitario e cosa invece può essere coperto da test funzionali o di integrazione . Se pratichi TDD in modo rigoroso per un anno, immagino che tu sia vicino a questo punto.

EDIT: sul tema della filosofia dei test unitari, penso che potrebbe essere interessante leggere: The Way of Testivus

E un punto più pratico, se non necessariamente molto utile,

  • Si parla di C ++ come linguaggio di sviluppo. Ho praticato TDD estensivamente in Java, usando librerie eccellenti come JUnit e Mockito. Tuttavia, ho trovato che TDD in C ++ è molto frustrante a causa della mancanza di librerie (in particolare, strutture di simulazione) disponibili. Anche se questo punto non ti aiuta molto nella tua situazione attuale, spero che tu lo prenda in considerazione prima di abbandonare del tutto il TDD.
risposta data 24.06.2011 - 02:35
fonte
8

Domanda molto interessante.

Ciò che è importante notare, è che C ++ non è facilmente testabile, e il gioco, in generale, è anche un cattivo candidato per TDD. Non è possibile testare se OpenGL / DirectX disegna un triangolo rosso con il driver X e giallo con il driver Y facilmente. Se il vettore normale della bump map non viene capovolto dopo la trasformazione dello shader. Né è possibile testare i problemi di clipping sulle versioni del driver con diverse precisioni e così via. Anche il comportamento di disegno non definito a causa di chiamate errate può essere verificato solo con una revisione accurata del codice e un SDK a portata di mano. Anche il suono è un cattivo candidato. Il multithreading, che di nuovo è abbastanza importante per i giochi, è praticamente inutile ai test unitari. Quindi è difficile.

Fondamentalmente il gaming è un sacco di GUI, audio e thread. La GUI, anche con componenti standard a cui è possibile inviare WM_, è difficile da testare.

Quindi, ciò che puoi testare sono le classi di caricamento dei modelli, le classi di caricamento delle texture, le librerie di matrix e alcune-tali, che non sono un sacco di codice e, molto spesso, non molto riutilizzabili, se è solo il tuo primo progetto. Inoltre, sono impacchettati in formati proprietari, quindi non è molto probabile che gli input di terze parti possano differire molto, a meno che non vengano rilasciati strumenti di modding ecc.

Quindi di nuovo, io non sono un guru o un evangelista del TDD, quindi prendi tutto ciò con un pizzico di sale.

Probabilmente scriverei alcuni test per i componenti principali principali (ad esempio la libreria di matrici, la libreria di immagini). Aggiungi un gruppo di abort() agli input non previsti in ogni funzione. E, soprattutto, concentrarsi su codice resistente / resiliente che non si rompa facilmente.

Riguardo a un errore, l'uso intelligente di C ++, RAII e un buon design fanno molta strada per prevenirli.

Fondamentalmente hai molto da fare solo per coprire le basi se vuoi rilasciare il gioco. Non sono sicuro che il TDD ti aiuterà.

    
risposta data 28.06.2011 - 15:39
fonte
6

Sono d'accordo con le altre risposte, ma voglio anche aggiungere un punto molto importante: costi di refactoring !!

Con test unitari ben scritti, puoi tranquillamente eseguire la riscrittura del tuo codice. In primo luogo, i test unitari ben scritti forniscono una documentazione eccellente sull'intento del vostro codice. Secondo, eventuali sfortunati effetti collaterali del tuo refactoring verranno catturati nella suite di test esistente. Pertanto, hai garantito che le ipotesi del tuo vecchio codice sono vere anche per il tuo nuovo codice.

    
risposta data 23.06.2011 - 14:00
fonte
4

How do other people survive TDD without killing all productivity and motivation?

Questo è completamente diverso dalle mie esperienze. Sei incredibilmente intelligente e scrivi codice senza bug, (ad esempio in caso di errori) o non ti accorgi che il tuo codice ha bug che impediscono il funzionamento del tuo programma, e quindi non sono in realtà finiti.

TDD significa avere l'umiltà di sapere che tu (e io!) commettiamo errori.

Per me il tempo necessario per scrivere le unittest è più che risparmiato in tempi di debug ridotti per i progetti che vengono fatti usando TDD sin dall'inizio.

Se non commetti errori, forse TDD non è così importante per te come lo è per me!

    
risposta data 24.06.2011 - 18:21
fonte
3

Ho solo alcune osservazioni:

  1. Sembra che tu stia provando a testare tutto . Probabilmente non dovresti, solo i casi ad alto rischio e limite di un particolare pezzo di codice / metodo. Sono abbastanza sicuro che la regola 80/20 si applica qui: passi l'80% dei test di scrittura per l'ultimo 20% del tuo codice o casi che non sono coperti.

  2. Dare la priorità. Entra nello sviluppo del software agile e fai un elenco di ciò che devi realmente fare per essere rilasciato in un mese. Quindi rilasciare, proprio così. Questo ti farà riflettere sulla priorità delle funzionalità. Sì, sarebbe bello se il tuo personaggio potesse fare un backflip, ma ha valore commerciale ?

Il TDD è buono, ma solo se non si mira al 100% di copertura del test, e non è se questo ti impedisce di produrre un effettivo valore di business (cioè caratteristiche che aggiungono qualcosa al tuo gioco).

    
risposta data 28.06.2011 - 17:06
fonte
1

Sì, scrivere test e codice potrebbe richiedere più tempo della semplice scrittura del codice, ma scrivere codice e test unitari associati (usando TDD) è molto più prevedibile della scrittura di codice e quindi del debugging.

Il debugging è quasi eliminato quando si utilizza TDD, il che rende tutto il processo di sviluppo molto più prevedibile e, alla fine, probabilmente più veloce.

Refactoring costante: è impossibile eseguire qualsiasi refactoring serio senza una suite di test unitaria completa. Il modo più efficiente per costruire quella rete di sicurezza basata su test unitari è durante il TDD. Il codice ben refactored migliora significativamente la produttività del designer / team che gestisce il codice.

    
risposta data 28.06.2011 - 05:24
fonte
0

Considera restringere l'ambito del tuo gioco e portarlo dove qualcuno può giocarlo o lo rilasci. Mantenere i tuoi standard di prova senza dover aspettare troppo a lungo per rilasciare il tuo gioco potrebbe essere una via di mezzo per motivarti. Il feedback dei tuoi utenti può fornire benefici a lungo termine e il tuo test ti consente di sentirti a tuo agio con le aggiunte e i cambiamenti.

    
risposta data 08.07.2011 - 02:17
fonte

Leggi altre domande sui tag