Sarò piuttosto radicale ma ti preghiamo di leggerlo completamente prima di criticare.
In breve: il design basato sui test è una specie di mito. È impossibile per l'implementazione anche per un nuovo codice, a meno che tutte le specifiche non siano già fisse e non cambino (è il classico caso "Waterfall" ma non "Agile", contrario all'area di applicazione TDD solitamente dichiarata). Quando funziona con progetti one-shot, funziona solo perché i suoi principi sono violati , non soddisfatti.
Per capire questo, si dovrebbe immaginare il caso in cui la specifica è cambiata e viene aggiunto un nuovo requisito, ma è già soddisfatto nel codice esistente. Il codice esistente dovrebbe essere eliminato e riscritto da zero? La descrizione TDD classica non può rispondere a tutto questo - non si occupa di un caso di codice esistente.
La seconda domanda è ciò che deve essere testato. Se un pezzo di codice deve soddisfare i test ma non fare nulla di più, potrebbe essere solo casi di test elencati in un grande switch-case ? Se non lo fa, perché? Questo è ciò che è esplicitamente richiesto (almeno nella classica spiegazione compatta).
La terza domanda è come controllare tutti i casi marginali. Se una routine moltiplica i numeri, deve controllare INT_MIN*INT_MIN
? La conoscenza dei casi marginali viene principalmente scoperta dopo alcune esperienze di utilizzo, ma non a priori . (Sì, maggiore è la competenza del programmatore, maggiore è la capacità di trovare tali casi prima di scrivere il codice che ha, ma il 90% delle competenze è ciò che non si deve fare, ma non quello che si deve.)
La mia risposta è che TDD svolge due ruoli:
- Mostra un ideale, irraggiungibile nella pratica (come l'infinito in matematica), ma è facile da comprendere e quindi approssimato il più vicino possibile.
- Fornisce una scusa ai manager per richiedere test che non vengono posticipati fino a "buoni tempi" ma solo ora, prima di riportare l'attività finita.
Quindi sarà trattato di conseguenza - "con entusiasmo ma senza fanatismo".
La mia attitudine personale al TDD è formata da un'esperienza specifica di costruzione di sistemi complessi che devono funzionare "24 * 7 * 365" e uno stile di accettazione che si avvicina al grado militare. Tali sistemi richiedono, da parte degli sviluppatori, test integrati delle catene di componenti e test regolari al volo, agli stessi componenti e con gli stessi algoritmi, ma con altri tag (quindi, solo il controllore delle decisioni finali comprende che non si trattava di dati reali) . Per i nostri sistemi, TDD è intenzionalmente sostituito con il seguente approccio:
- Naturalmente, il principio secondo cui il codice deve fare ciò a cui è destinato, deve essere più importante del principio TDD (o analogico) che un codice sia il più piccolo e semplice possibile. Ciò significa anche che la leggibilità del codice e la debugabilità sono molto importanti. (Tutto questo non è un commento di Captain Ovvio ma la regola scritta per quei ragazzi che tendono a seguire le istruzioni nel modo più letterale e che, purtroppo, sono presenti in ogni grande squadra.)
- Ogni componente inizia con un set di test ragionevolmente piccolo che consente a un programmatore esperto di dire che è stato controllato, in base alle condizioni e alle risorse correnti.
- Una parte del tempo (di ciascun programmatore separatamente o dell'intero team) è dedicata all'espansione di test per componenti già esistenti. Questo può essere, secondo le specifiche degli obiettivi, dal 20% all'80%, ma sicuramente non inferiore. Questo lavoro non si fermerà fino a quando l'intero sviluppo del progetto non verrà abbandonato.
- Per rispondere alla domanda se un test funziona (a cui risponde TDD in modo univoco e invalicabile), il test stesso deve avere tecniche per convalidarlo. Per questo compito, usiamo le cosiddette inversioni . Un'inversione è una modifica dei dati o dell'ambiente in entrata che deve interrompere il test in modo noto. Ad esempio, se una funzione converte A1 in B1 e A2 in B2, il suo test che convalida B1 è la risposta ad A1 avrà esito negativo se il valore di input diventa A2 e, inoltre, è possibile controllare i dettagli di errore (tipo di errore generato, numero di errori, ecc.) Non tutti i test devono avere inversioni; ma più è complesso, più inversioni principali sono.
P.S. Per una guida iniziale per aggiungere test a un progetto esistente, è possibile iniziare con M. Il libro di Feather . Non si basa su TDD ma, invece, è molto pratico nel suggerire come riformare un codice esistente in modo evolutivo.