TDD, UnitTesting ed il significato di "qualità del software".

Il post di Raf ha già espresso in modo particolarmente chiaro molti concetti che condivido perfettamente.

Lo so... sembra pecoronismo scrivere "agganciandosi" al trenino, ma in realtà dopo i CDII ho evitato di postare in merito all'argomento per evitare di "prolungare" il dibattito.

Ci ero riuscito, anche dopo aver letto il post di Raf, ma il commento di Claudio mi ha... "stappato"!

Avevo già espresso in passato le mie impressioni personali su Unit Testing (promosso), filosofia delle metodologie Agili (promossa), TDD (bocciato).

Se quello di cui stiamo parlando sono applicazioni sufficientemente complesse (non di script e simili), ci sono cose scritte in quei commenti che a mio personale avviso sono sbagliate e vorrei sottolinearle in modo "costruttivo" (almeno nelle mie intenzioni).

Per come la vedo io (e sono assolutamente aperto ad obiezioni motivate):

 

1) Lo Unit Testing è uno strumento molto utile per prevenire "alcune tipologie di bug" che potrebbero essere introdotti involontariamente durante lo "sviluppo", per esempio quelli logici, matematici o di interfacciamento con altri sistemi.

Il compilatore è uno strumento potente per individuare a compile-time errori di "programmazione", la cui inviduazione verrebbe invece posticipata al primo utilizzo a run-time (si spera da parte dello sviluppatore o del tester) di quella porzione di codice.

Il compilatore abbinato all'OOP è uno strumento molto potente che ci garantisce l'assenza di utilizzi errati di ogni tipo di oggetto (certo, se poi sbaglio a "castare" oppure uso sempre Object... ).

Il compilatore abbinato al DDD in una architettura (seria) n-tier è uno strumento potentissimo per garantire la robustezza di un software, che per quel che mi riguarda è uno dei pilastri, se non il più importante, del concetto di qualità del software.

 

Solito piccolo esempietto che più o meno tutti conosciamo:

Quanti test servirebbero per coprire questa funzione non tipizzata e, soprattutto, sarebbe possibile verificare se il risultato è quello che si aspetta il chiamante? 

Sum(a,  b)
{
      return a + b;
}

Se il chiamante per una svista dello sviluppatore inviasse i parametri 1 e 2 come string e poi (ovviamente) interpretasse il risultato come numerico avremmo un bel casino (non un errore!).

Al contrario, da qui non ci si scappa:

Int32 Sum(Int32 a, Int32 b)
{
      return a + b;
}

Int32 Sum(string a, string b)
{
      return Int32.Parse(a) + Int32.Parse(b);
}

 

2) Qui siamo al punto più difficile da "far leggere fino in fondo": premesso che rispetto comunque in modo assoluto chi applica questa metodologia (sto esprimento la mia idea in merito di industria del software, non di gusti personali) secondo me sviluppare in "TDD puro" sempre e comunque è semplicemente sbagliato per almeno 2 motivi!

Accetto tutte le critiche (costruttive, altrimenti me ne faccio poco ) ma vorrei prima spiegare le mie ragioni sperando che eventuali "TDDers" provino a  leggerle in modo "oggettivo".

Sapendo di non essere in grado di dare una definizione corretta di TDD mi affido a quella di Wikipedia:

Test-Driven Development (TDD) is a software development technique that involves repeatedly first writing a test case and then implementing only the code necessary to pass the test.

a) Il codice scritto in questo modo garantisce solo che il bug prefigurato dal test iniziale non si verifichi, che di per sè vale veramente poco se applicato universalmente in qualsiasi scenario!

b) La robustezza di un software non è l'unico aspetto da prendere in considerazione quando scriviamo codice. Scrivere codice col fine di far passare il test è una cosa, scrivere codice ragionandoci su in modo da ottimizzarlo, renderlo gestibile ed anche comprensibile è tutta un'altra cosa!

TDD mi sembra (ma è solo una sensazione personale) un remake del vecchio detto "Non rimandare a domani ciò che puoi fare oggi!" per incentivare gli sviluppatori a non tralasciare i test dopo aver scritto il codice (e su questo sono io il primo "peccatore"). Sicuramente apprezzabile come indicazione preziosa, non come metodologia di sviluppo.

Sempre a mio avviso, esistono comunque degli scenari "estremi" (vi dice qualcosa quella parola?) dove l'applicazione del TDD può realmente risultare decisiva. Non apro il discorso per non andare OT e sottolineo il fatto che si tratta di alcuni scenari, non tutti.

 

Arriviamo al commento del buon Claudio (che mi ammazzerà dopo questo post ), pieno di spunti interessanti...

"Concordo anche sul fatto che testare a posteriori serve a molto poco"

Secondo me serve eccome! Se si evidenzia un bug nel software (anche in un pezzo di codice coperto da test, ricordiamocelo sempre!!!), significa che quello è un "punto debole" del software stesso. E' vero che una volta crollato il muro lo si rinforza il triplo , ma comunque potrebbe rivelarsi un "nodo" delicato del software e quindi ha sicuramente molto senso scrivere un test (se non esisteva) o aggiungerne altri che contemplano possibili scenari alternativi.

"Con TDD [...] il codice prodotto è qualitativamente migliore del codice prodotto senza."

Altra cosa su cui non sono assolutamente d'accordo! Il codice non è di qualità solo perchè è stato scritto un test che viene superato! Non voglio essere ripetitivo, ma la qualità di un codice è qualcosa di molto, molto più ampio.

Secondo me si può tutt'al più affermare che scrivere Unit Tests può aiutareverificare la qualità del codice.

"Quindi pair e TDD sono solo all'apparenza approcci più dispendiosi di un approccio "senza metodi agili"

Metodologie Agili non significano solo TDD e pair programming ed a mio avviso non si possono accreditare a TDD tutti i benefici che derivano dall'approccio Agile.

"Inoltre all'aumentare della complessità dell'applicazione la gestione del cambiamento del software cresce in maniera esponenziale mentre con strumenti di test automatici si può mantere entro un livello accettabile il costo di gestione del cambiamento."

Su questo concordo ma rilancio con una domanda: se il software è sufficientemente componentizzato e disaccoppiato, se i limiti ed i "contratti" di ogni sottosistema e di ogni parte del sottosistema sono ben definiti, se gli spaghetti li vediamo solo a tavola nel piatto, se la frase "ok, facciamo che quando..." ce la siamo scordata da anni, quel software risulta davvero così difficile da manutenere ed espandere?

"Altro elemento fondamentale da non trascurare è la fiducia del committente.
Codice di qualità permette di raggiungere un'altro obbiettivo importate: guadagnare la fiducia del committente."

Concordo anche in questo caso e sottoscrivo, ma... ancora una volta torniamo al punto cruciale: cosa significa guadagnare la fiducia del committente? Significa fargli vedere 2400 cerchiolini verdi che si accendono (lo dico in tono scherzoso ma realistico) oppure significa che dopo aver consegnato il prodotto in fase finale, negli anni a seguire questo continua a funzionare egregiamente senza bug o errori "strani"? (perchè catchare un errore e visualizzare il messaggio non significa sempre che "il software funziona bene")

Sento tante persone testimoniare che hanno ridotto drasticamente il numero di bug e l'utilizzo del debugger da quando seguono il TDD e mi chiedo in che modo sviluppassero codice prima di applicare il TDD.

Intendiamoci, non è una critica, ma mi viene il dubbio che il TDD sia stato in molti casi un valido cerotto per chi, a causa di altri "punti deboli" (magari nati da motivazioni comprensibili come la pressione delle scadenze, la fretta, la frammentazione del team, ecc...) nella metodologia di sviluppo, avesse un'emorragia di bug difficilmente arginabile!

Personalmente posso affermare che l'argomento "bugs" per me non è più un problema vero da diversi anni (in realtà anche ai tempi di VB "legacy" non ne soffrivo un gran che), non per merito mio ma degli insegnamenti e dell'esperienza di tante altre persone che mi hanno aiutato in questi anni ad imparare a scrivere codice di qualità infinitamente superiore rispetto al passato e soprattutto basato su una solida architettura (azz, l'ho scritto...).

Print | posted @ martedì 3 luglio 2007 20:54

Comments on this entry:

Gravatar # re: TDD, UnitTesting ed il significato di "qualità del software".
by Marco Abis at 03/07/2007 22:46

Rispettando le opnioni di tutti credo ci sia un grosso fraintendimento alla base del discorso TDD e gira attorno al significato letterale della "T". Il TDD e' una tecnica di design evolutivo che ha come benefico side-effect la costruzione di una suite di test di regressione.

Ma, appunto, e' un side-effect perche' a tutti gli effetti il TDD serve per fare design, non per testare!!

E' per questo motivo che sono sorte proposte di nomenclatura alternativa come BDD: Behaviour-Driven Development anche se col tempo il concetto di BDD e' sta' maturando andando oltre al semplice "TDD dove la T e' sostituita da una B" http://behaviour-driven.org/ e in particolare http://dannorth.net/introducing-bdd/

E comunque, in ogni caso, non esistono silver bullet e vado oltre: non credo nelle best pratice. Piuttosto mi trovo a mio agio con delle "good practice in a context" :-)
Gravatar # Unit testing non
by makka at 04/07/2007 00:09

Gravatar # re: TDD, UnitTesting ed il significato di "qualità del software".
by Mario Duzioni at 04/07/2007 01:24

Grazie Marco, scrivendo il post ovviamente mi sei venuto in mente anche te, ma non avrei osato sperare addirittura in qualche tua "dritta". :-)

Molto interessante ciò che scrivi. Devo anche dire che in effetti però forse è la stragrande maggioranza a fraintendere il significato di TDD.

Spero di trovare il tempo di approfondire tramite i tuoi links.

Ciao!
Gravatar # re: TDD, UnitTesting ed il significato di "qualità del software".
by Mario Duzioni at 04/07/2007 01:49

Ovviamente grazie (lo davo per scontato) anche a Roby per il commento...
Comments have been closed on this topic.