View Full Version : [Vari] TDD Ma è davvero sviluppo agile?
tomminno
11-11-2008, 12:48
Come si fa a definire agile lo sviluppo TDD?
In un sistema di una certa dimensione non si può sviluppare un TDD senza Mock perchè un applicativo per funzionare spesso si basa sul fatto che dietro c'è un mondo intero di programmi che lavora ed è impensabile di riscrivere la logica di tutti gli altri applicativi solo per creare i dati corretti ad esempio su db nel SetUp e cancellazione nel TearDown.
E come si fa agilmente a far si che una classe al suo interno richiami il Mock invece del vero codice?
A quanto ho definitivamente sperimentato i test servono solo a dimostrare che il flusso logico interno è corretto, chiaramente tramite i Mock, ma non mi segnaleranno mai se una qualche modifica nel sistema, per sbaglio, causa il mancato funzionamento del programma, proprio perchè con i Mock mi sono isolato dal sistema: si so che il programma funziona, ma non so se ad un certo punto il programma smette di funzionare per cause esterne. A che cavolo servono i test?
Alla fine perdo tempo a scrivere test che so che verranno passati e non verificherò mai delle condizioni per cui questo non avviene.
Insomma una perdita di tempo?
Secondo me si... già a logica, l'idea di scrivere per ogni classe un test che simula ogni possibilie situazione, mi pare una cosa lunga, imprecisa, difficile, e che ruba un sacco di tempo che potresti dedicare a correggere con le tue manine, senza contare che incasina ulteriormente il codice e lo rende immantenibile.
Senza mezzi termini, mi pare una gran cavolata... ci sono metodi molto migliori per essere sicuri che il codice funziona, ma non sono affatto "agili" o "automatici"... per esempio capire cosa stai scrivendo.
Secondo me si... già a logica, l'idea di scrivere per ogni classe un test che simula ogni possibilie situazione, mi pare una cosa lunga, imprecisa, difficile, e che ruba un sacco di tempo che potresti dedicare a correggere con le tue manine, senza contare che incasina ulteriormente il codice e lo rende immantenibile.
Senza mezzi termini, mi pare una gran cavolata... ci sono metodi molto migliori per essere sicuri che il codice funziona, ma non sono affatto "agili" o "automatici"... per esempio capire cosa stai scrivendo.
Ti ci voglio vedere dopo a spendere il doppio del tempo per fare debugging. Il tempo che perdi nello scrivere e manutenere i test lo riacquisti risparmiandoti quasi totalmente il debugging durante lo sviluppo e risparmiando buona parte del debugging per il bug fixing ;)
Programmare in TDD non deve rendere o rendere immantenibile il codice, anzi lo deve rendere più semplice, più snello e manutenibile.
Il TDD inoltre non deve simulare qualsiasi situazione ;) Assolutamente no, deve solo coprire il codice. Il che vuol dire coprire ogni ramo di if all'interno del codice.
E come si fa agilmente a far si che una classe al suo interno richiami il Mock invece del vero codice?
Sfruttando la programmazione per interfacce ;)
class QueryInterface
{
public:
QueryInterface();
virtual QueryResult doQuery() = 0;
};
class MockQuery: public QueryInterface
{
bool queryDone;
public:
MockQuery(): queryDone(false);
QueryResult doQuery()
{
queryDone = true;
return SomeWorkQueryResult();
}
bool wasQueryDone() { return queryDone; }
};
class ConcreteQuery: public QueryInterface
{
public:
ConcreteQuery();
QueryResult doQuery();
};
Nel test per testare se la mia classe ha fatto la query:
MockQuery * query = new MockQuery();
MyClass instance(query);
instance.doSomeWork();
AssertFalse(query->wasQueryDone());
Nel codice che istanzia MyClass:
MyClass *instance = new MyClass(new ConcreteQuery());
A quanto ho definitivamente sperimentato
Addirittura ? :D
i test servono solo a dimostrare che il flusso logico interno è corretto, chiaramente tramite i Mock, ma non mi segnaleranno mai se una qualche modifica nel sistema, per sbaglio, causa il mancato funzionamento del programma, proprio perchè con i Mock mi sono isolato dal sistema: si so che il programma funziona, ma non so se ad un certo punto il programma smette di funzionare per cause esterne. A che cavolo servono i test?
Servono appunto a dimostrare che il codice che hai scritto è corretto. I test danno per scontato che il sistema sotto Mock funzioni correttamente. Non sono i test a dover testare il sistema esterno.
Se il sistema smette di funzionare puoi (quasi) SICURAMENTE escludere che si tratti di un problema di codice. Ti sembra poco ?
Alla fine perdo tempo a scrivere test che so che verranno passati e non verificherò mai delle condizioni per cui questo non avviene.
Non è vero, dopo che un test che diventa verde, quel test può fallire molto spesso:
- durante lo sviluppo le modifiche al codice fanno fallire un test
- durante il bugfixing le modifiche al codice fanno fallire un test
Prima di tutto se un test diventa rosso so già dove andare a guardare per correggere il problema. Queste sono situazioni fondamentali, in quanto andando a modificare il codice in modo da far ritornare al successo il test, la test base mi garantisce di non aver introdotto altri errori noti (bug fixati precedentemente), ma sopratutto mi garantisce a priori che le funzionalità fondamentali del programma (che sono oggetto di test) siano garantite.
banryu79
11-11-2008, 17:26
Ma, forse non e poi una perdita di tempo se consideriamo che questa metodologia di sviluppo è stata usata con successo per esempio in contesti di sviluppo complessi in codice di dimensioni delle MLOC (vedi Fek, ad esempio, non penso che uno con la sua esperienza ci venga a raccontare frescacce. Poi ci sono anche i ragazzi di Diamond Crush che potrebbero avere qualcosa di interessante da dire in merito).
Ti ci voglio vedere dopo a spendere il doppio del tempo per fare debugging. Il tempo che perdi nello scrivere e manutenere i test lo riacquisti risparmiandoti quasi totalmente il debugging durante lo sviluppo e risparmiando buona parte del debugging per il bug fixing ;)
Programmare in TDD non deve rendere o rendere immantenibile il codice, anzi lo deve rendere più semplice, più snello e manutenibile.
Il TDD inoltre non deve simulare qualsiasi situazione ;) Assolutamente no, deve solo coprire il codice. Il che vuol dire coprire ogni ramo di if all'interno del codice.
Solo ogni ramo di IF? :asd:
In un gioco di medie dimensioni ci saranno una quantità di rami di if contabili sulle migliaia... senza contare l'esplosione combinatoria data da un'uso efficace del polimorfismo.
E se ci infilo anche Lua?
IMHO è una cosa totalmente fuori dal mondo, per quanto possa essere utile a contenere le incomprensioni all'interno di un team.
Senza contare:
- che tutto questo apparato pazzesco riesce solo a dirti che c'è un errore, mica qual'è o dove si origina...
-che il 90% degli errori in C++ sono errori di nullpointers e simili, e il crash del programma fa capire piuttosto bene che c'è un errore...
-che se l'errore è nell'algoritmo, molto probabilmente sarà sbagliato anche l'algoritmo di controllo: mettiamo che io voglia avere il risultato del prodotto di matrici, ma sbaglio a scrivere perchè non so la teoria.
Ebbene, mi faccio il mio bravo test che trova errori in base ad un pò di prodotti svolti da me su carta... solo che sono sbagliati anche quelli su carta! In questa maniera l'errore non lo troverai mai, se ti fidi del test...
-Come fai a trovare un test buono per algoritmi inventati da te? Che ne sai che sono i migliori possibili?
per questo, boh, anche se funzionasse mi pare un grosso spreco di tempo in applicazioni che non devono essere indistruttibili, in quanto più che trovare gli errori serve a "congelare" la codebase...
Solo ogni ramo di IF? :asd:
In un gioco di medie dimensioni ci saranno una quantità di rami di if contabili sulle migliaia... senza contare l'esplosione combinatoria data da un'uso efficace del polimorfismo.
Ogni if se vuoi ottenere una copertura totale.
Hai appunto nominato il polimorfismo che consente di limare all'osso la complessità ciclomatica del codice. Ogni classe può essere testata per il suo funzionamento, il comportamento generale del codice con le classi polimorfiche non è detto che vada calcolato singolarmente.
Ad esempio: faccio una classe che contiene una collection di figure piane. Inserisco nella classe un quadrato e due rettangoli e testo il metodo che mi calcola la somma delle superfici.
Ora voglio sviluppare anche un centinaio di classi derivate, ogni classe derivata ovviamente mi ritorna l'area, basta che io testi la correttezza dell'area ritornata ed automaticamente non c'è bisogno che io testi ancora il metodo che mi ritorna la somma delle aree inserendo tutti i vari tipi di figura nella collection. Il test sulla somma resta invariato, devo solo controllare la correttezza del calcolo dell'area delle singole figure.
Senza contare:
- che tutto questo apparato pazzesco riesce solo a dirti che c'è un errore, mica qual'è o dove si origina...
No, molto spesso ti dice dov'è (porzione del codice che viene testato e test che falliscono) e il tipo di errore considerando quale test passa e quale no.
-che il 90% degli errori in C++ sono errori di nullpointers e simili, e il crash del programma fa capire piuttosto bene che c'è un errore...
Certo, perché non si scrivono i test per le classi o le funzioni che gestiscono la memoria. Fai un allocatore ed un deallocatore ed imponi che nel codice non si possano usare new e delete direttamente. Conta la memoria allocata e la memoria deallocata. Prima della creazione e dopo della distruzione della classe, per il principio secondo cui chi alloca la memoria la deve anche deallocare, la memoria allocata deve essere la stessa.
-che se l'errore è nell'algoritmo, molto probabilmente sarà sbagliato anche l'algoritmo di controllo: mettiamo che io voglia avere il risultato del prodotto di matrici, ma sbaglio a scrivere perchè non so la teoria.
No, perché l'algoritmo di controllo nel 90% dei casi non deve essere un algoritmo, altrimenti se usi lo stesso algoritmo sia per il controllo che per la produzione del risultato hai violato il principio di duplicazione del TDD.
I tre passi fondamentali del TDD sono scrivere un test che fallisce, scrivere il codice che fa passare il test, eliminare le duplicazioni sia di codice che di dati.
Ebbene, mi faccio il mio bravo test che trova errori in base ad un pò di prodotti svolti da me su carta... solo che sono sbagliati anche quelli su carta! In questa maniera l'errore non lo troverai mai, se ti fidi del test...
Imho non ha senso questo discorso. Anche quando andrai a visualizzare il risultato con un debugger lo troverai errato perché i conti su carta sono errati, qui non c'è metodologia di sviluppo che ti possa salvare.
-Come fai a trovare un test buono per algoritmi inventati da te? Che ne sai che sono i migliori possibili?
I test non testano che un algoritmo sia il migliore possibile, ne testa solo la correttezza. Poi dipende che intendi per migliore: il più veloce ? il meno avaro di memoria ? il più leggibile ?
Però i test ti consento di prendere in mano l'algoritmo, rifattorizzarlo, modificarlo e poi vai a far girare la test base e ne verifichi la correttezza ;)
per questo, boh, anche se funzionasse mi pare un grosso spreco di tempo in applicazioni che non devono essere indistruttibili, in quanto più che trovare gli errori serve a "congelare" la codebase...
Spiega meglio...
DanieleC88
11-11-2008, 18:55
Non sono un grande sviluppatore né ho avuto l'occasione di sperimentare molto il TDD su progetti di grossa/media dimensione, ma posso dire che è molto utile in certi casi avere una serie di test che verificano rigidamente la correttezza del software; appena fai una modifica che mette in crisi i test puoi subito porre rimedio invece di allargare l'errore in modo catastrofico.
Ad esempio, in un progetto universitario che ho sviluppato l'anno scorso, pur di modeste dimensioni, ad un certo punto ho sentito il "bisogno" di controllare che ogni funzione stesse lavorando nel modo che io mi aspettavo, e non diversamente. Purtroppo ho inserito i test solo in un secondo momento, quindi non è stato vero TDD fino a quel punto, ma poi ho dovuto perdere molto meno tempo nel debugging rispetto a quanto ne avevo speso prima.
Detto questo, lascio spazio ai più esperti, io non ho molto titolo per potermi pronunciare su queste cose. ;)
A quanto ho definitivamente sperimentato i test servono solo a dimostrare che il flusso logico interno è corretto, chiaramente tramite i Mock, ma non mi segnaleranno mai se una qualche modifica nel sistema, per sbaglio, causa il mancato funzionamento del programma, proprio perchè con i Mock mi sono isolato dal sistema: si so che il programma funziona, ma non so se ad un certo punto il programma smette di funzionare per cause esterne. A che cavolo servono i test?
Se vuoi simulare situazioni impreviste basta creare Mock che falliscono in maniera ripetibile. Se vuoi testare come si comporta la tua applicazione in caso di un timeout durante un invio di dati via rete non devi far altro che creare un socket che fallisce dopo l'invio di tot byte ad esempio. Non devi nemmeno scrivere tutta la logica di un socket ma solo il minimo indispensabile per far girare il test. Ci sono anche librerie apposite come jmock che rendono la creazione di mock veramente banale.
Insomma una perdita di tempo?
Per quanto mi riguarda no. Sono una rete di sicurezza inestimabile da usare ad ogni modifica al codice e da aggiornare continuamente ad ogni bugfix.
Se vuoi simulare situazioni impreviste basta creare Mock che falliscono in maniera ripetibile. Se vuoi testare come si comporta la tua applicazione in caso di un timeout durante un invio di dati via rete non devi far altro che creare un socket che fallisce dopo l'invio di tot byte ad esempio. Non devi nemmeno scrivere tutta la logica di un socket ma solo il minimo indispensabile per far girare il test. Ci sono anche librerie apposite come jmock che rendono la creazione di mock veramente banale.
Ah ecco che intendeva :D
Ci sono anche per C++: http://mockpp.sourceforge.net/
*
Mi hai fatto ricredere in parte :asd:
Cmq continuo a nutrire dei dubbi sulla convenienza e l'efficacia... da quello che ho capito è migliore in situazioni dove il programma è sottoposto a moli di input variabili, e deve mantenere una stabilità comprovabile... che so, applicativi manageriali ecc.
Per un gioco invece mi pare che non ci sarebbe davvero il modo di fare un test che valida il tutto, perchè l'obiettivo da raggiungere è interno all'applicativo stesso... come faresti a verificare se un algoritmo che disegna forme col mouse è giusto?
Gli occhi mi sembrano ancora il debugger migliore :asd:
Per un gioco invece mi pare che non ci sarebbe davvero il modo di fare un test che valida il tutto, perchè l'obiettivo da raggiungere è interno all'applicativo stesso... come faresti a verificare se un algoritmo che disegna forme col mouse è giusto?
Gli occhi mi sembrano ancora il debugger migliore :asd:
Il movimento di un mouse può essere registrato e riprodotto infinite volte. L'output a video di un programma rediretto ad un file per fare un confronto con file che si è certi essere corretti. Firefox ha una suite con decine di migliaia di test per il redering delle pagine web che vengono eseguiti ad ogni build interna per controllare che le modifiche giornaliere non abbiano introdotto regressioni.
Gli occhi umani possono essere ingannati e il giudizio sarà sempre soggettivo mentre ci sono algoritmi che possono dirti che l'ultima modifica al codice ha peggiorato la qualità dell'immagine di 0.2 dB
Cmq continuo a nutrire dei dubbi sulla convenienza e l'efficacia... da quello che ho capito è migliore in situazioni dove il programma è sottoposto a moli di input variabili, e deve mantenere una stabilità comprovabile... che so, applicativi manageriali ecc.
Quali sono i programmi che non sono sottoposti a moli di input non variabili ? Direi molto pochi ;)
Per un gioco invece mi pare che non ci sarebbe davvero il modo di fare un test che valida il tutto, perchè l'obiettivo da raggiungere è interno all'applicativo stesso... come faresti a verificare se un algoritmo che disegna forme col mouse è giusto?
Tutt'altro, come già detto da VICIUS, puoi benissimo validare ad esempio lo stato della viewport dispetto a degli input sintetici ;) Fai un mock della tastiera e uno del mouse, inserisci degli eventi prefissati, o addirittura generati casualmente e verifichi passo passo o alla fine lo stato delle variabili interne. In un gioco potrebbe essere la posizione all'interno di un mondo 3D.
Pensa che in Diamond questa costituiva una delle parti fondamentali ;)
Gli occhi mi sembrano ancora il debugger migliore :asd:
Fino a quando non perdi mezza giornata dietro ad un bug che, dopo, ti accorgi che avrebbe potuto essere esercitato con un test composto da tre righe di codice ;)
Prova a pensarci non appena fai una intesa sessione di debug :D
E anche importante non confondere la test base con il TDD, la maggior parte dei programmi commerciali e non ha complesse suite di test scritti a diversi livelli dopo ogni modulo del programma e dopo ogni bugfix. Quindi non siamo qui a discutere sull'efficacia ormai comprovata di un'ampia test base, ma sul fatto di scrivere il test prima del codice che lo rende verde...è questo il TDD ;)
Anche i giochi sviluppati attualmente hanno test base da diverse decine di migliaia di test, nonostante non siano scritti in TDD.
tomminno
12-11-2008, 07:46
Sfruttando la programmazione per interfacce ;)
Questo significa però che in ogni classe devo prevedere un costruttore che accetta in ingresso tutti gli oggetti usati internamente.
Già si comincia a modificare il codice per eseguire i test.
Inoltre questo comporta che non sono applicabili a codice già esistente.
Servono appunto a dimostrare che il codice che hai scritto è corretto. I test danno per scontato che il sistema sotto Mock funzioni correttamente. Non sono i test a dover testare il sistema esterno.
Se il sistema smette di funzionare puoi (quasi) SICURAMENTE escludere che si tratti di un problema di codice. Ti sembra poco ?
Però non mi dicono se il codice (corretto ovviamente ;) ) che ho scritto ad un certo punto smette di funzionare all'interno di un sistema più grande per modifiche all'ambiente esterno.
L'unica è il test d'integrazione che però spesso porta a tempi di sviluppo che sono almeno tripli se non inaccettabilmente più grandi del tempo di sviluppo dell'applicativo. Alla faccia dell'agile.
Non è vero, dopo che un test che diventa verde, quel test può fallire molto spesso:
- durante lo sviluppo le modifiche al codice fanno fallire un test
- durante il bugfixing le modifiche al codice fanno fallire un test
Il problema dove lavoro è che quei test generalmente diventano (o dovrebbero diventare) rossi a causa di modifiche all'ambiente esterno, non a causa di bug del codice, perchè tutti i software si inseriscono in un flusso ben più ampio del singolo applicativo
Prima di tutto se un test diventa rosso so già dove andare a guardare per correggere il problema. Queste sono situazioni fondamentali, in quanto andando a modificare il codice in modo da far ritornare al successo il test, la test base mi garantisce di non aver introdotto altri errori noti (bug fixati precedentemente), ma sopratutto mi garantisce a priori che le funzionalità fondamentali del programma (che sono oggetto di test) siano garantite.
Ma non ti garantiscono che il tuo software continui a funzionare in un ambiente di produzione.
tomminno
12-11-2008, 07:53
Se vuoi simulare situazioni impreviste basta creare Mock che falliscono in maniera ripetibile. Se vuoi testare come si comporta la tua applicazione in caso di un timeout durante un invio di dati via rete non devi far altro che creare un socket che fallisce dopo l'invio di tot byte ad esempio. Non devi nemmeno scrivere tutta la logica di un socket ma solo il minimo indispensabile per far girare il test. Ci sono anche librerie apposite come jmock che rendono la creazione di mock veramente banale.
Ma il mock è qualcosa che ti isola dal sistema, sei te a dire cosa e come deve fallire o meno. E in più ti sei isolato dal sistema. Un esempio banale, se qualcuno cambia su db il valore predefinito di una colonna cosa succede all'applicativo? E se qualcuno cambia una stored procedure facendogli accettare valori diversi da quelli previsti dal software?
Il test continua ad essere verde, ma il software non funziona più.
Riesco a monitorare il software che smette di funzionare?
Perchè in un sistema con migliaia di programmi ci vuole qualcosa che ti dica se il tuo software continua a funzionare anche a seguito di modifiche all'ambiente.
Questo significa però che in ogni classe devo prevedere un costruttore che accetta in ingresso tutti gli oggetti usati internamente.
Già si comincia a modificare il codice per eseguire i test.
Inoltre questo comporta che non sono applicabili a codice già esistente.
Non è detto, se MyClass lavora su diverse istanze di QueryInterface è una funzionalità richiesta. Non tutti gli oggetti usati internamente, ma solo quelli mockati. In ogni caso esistono i factory method od i costrutturi polimorfici ;)
Stiamo parlando di TDD e non di scrivere i test per codice già scritto. C'è una differenza notevole.
Però non mi dicono se il codice (corretto ovviamente ;) ) che ho scritto ad un certo punto smette di funzionare all'interno di un sistema più grande per modifiche all'ambiente esterno.
Il problema dove lavoro è che quei test generalmente diventano (o dovrebbero diventare) rossi a causa di modifiche all'ambiente esterno, non a causa di bug del codice, perchè tutti i software si inseriscono in un flusso ben più ampio del singolo applicativo
Ma non ti garantiscono che il tuo software continui a funzionare in un ambiente di produzione.
Il malfunzionamento si può simulare con i Mock object. Ma non è compito dei test testare i sistemi esterni. Ripeto è un discorso che non c'entra assolutamente niente. Nemmeno se ti metti a lavorare ore con il debugger hai la garanzia che i sistemi esterni funzionino.
Volendo anche la verifica della correttezza delle tabelle del DB è fattibile (quale problema c'è a farla ???). Si tratta solo di non mockare il DB e tenerlo accessibile durante tutte le operazioni.
Il Mock si fa principalmente per tutti quei sistemi in cui la verifica è scontata o comunque non fattibile. Ad esempio: come fai a dire se un file audio ha suonato ? Oppure...come fai a dire se l'immagine viene visualizzata a video ?
Il Mock del DB era solo un esempio, che comunque può servire a prescindere che si faccia o meno uso del DB durante lo sviluppo, ma volendo si può testare tranquillamente che le tabelle siano quelle prefissate, in caso cambino inaspettatamente o nel caso in cui una query ritorni attributi non attesi.
Perchè in un sistema con migliaia di programmi ci vuole qualcosa che ti dica se il tuo software continua a funzionare anche a seguito di modifiche all'ambiente.
Non tutti i progetti sono di questo tipo ;) Stai facendo un'assunzione specifica e da questo deduci che il TDD non funzioni. Come dire: per me non è adatta, quindi non funziona. Nessuno sta dicendo che sia la panacea di tutti i mali della programmazione, ma solamente che imho ne risolve buona parte :D
Riporto qua un post dall'oltretomba di TigerShark, che mi sembrava troppo chiaro per relegarlo ad un PM :asd:
comunque..
Ci sono diverse suite pensate apposta per testare l'interfaccia grafica..
ma il problema a monte è un altro.
Il TDD usa degli unit test, invece le obiezioni che hai postato nel primo messaggio erano relative ai test funzionali.
Quindi, a quasto punto, mi sa che non hai ben chiara la gerarchia di test che divide in test in:
- unit test (al livello + profondo)
- integration test (test funzionali)
- system test (test globali)
Il tuo primo post, a quanto ho capito, si riferiva soprattutto alla fase di integration test e di system test.
Quote:
Integration Test
The purpose of integration testing is to verify functional, performance and reliability requirements placed on major design items. These "design items", i.e. assemblages (or groups of units), are exercised through their interfaces using black box testing, success and error cases being simulated via appropriate parameter and data inputs. Simulated usage of shared data areas and inter-process communication is tested and individual subsystems are exercised through their input interface. Test cases are constructed to test that all components within assemblages interact correctly, for example across procedure calls or process activations, and this is done after testing individual modules, i.e. unit testing.
The overall idea is a "building block" approach, in which verified assemblages are added to a verified base which is then used to support the integration testing of further assemblages.
Quote:
System Test
System testing of software or hardware is testing conducted on a complete, integrated system to evaluate the system's compliance with its specified requirements. System testing falls within the scope of black box testing, and as such, should require no knowledge of the inner design of the code or logic. [1]
As a rule, system testing takes, as its input, all of the "integrated" software components that have successfully passed integration testing and also the software system itself integrated with any applicable hardware system(s). The purpose of integration testing is to detect any inconsistencies between the software units that are integrated together (called assemblages) or between any of the assemblages and the hardware. System testing is a more limiting type of testing; it seeks to detect defects both within the "inter-assemblages" and also within the system as a whole.
Le tue obiezioni erano infatti perfettamente fondate, ma il fatto è che dove non arrivano gli unit test servono appunto gli integration e system test.
E' solo l'insieme di questi test a dare la sicurezza di avere un sistema perfettamente funzionante.
Il solo TDD da la sicurezza che il codice scritto in ogni metodo rispetti i requisiti per cui quel metodo è stato pensato.
Ovviamente il TDD da solo è comunque un grosso miglioramento, ma non è la soluzione ottimale perchè non serve a individuare i problemi dell'intero sistema che sono delegati a metodoloige di test completamente diverse
Cmq, io continuo a pensare che tutto ciò possa si essere utile, ma spesso è molto meglio impegnarsi nella sinergia del team e nella documentazione, più che costruire un'imbragatura pazzesca che ti impedisce di spararti nei piedi...
Io, in quanto one-man-team, ho difficilmente avuto bisogno di una cosa del genere; la maggior parte degli errori sono sempre dovuti a nullpointers sparsi, quindi facilmente rintracciabili; gli altri spesso ad errori negli algoritmi, che fanno cose nell'ordine sbagliato/o sono semplicemente errati. Si vede visivamente che gli oggetti non fanno quello che devono fare, e si deduce la parte del codice fallata. Se uno si ricorda quello che ha scritto non è poi così difficile :asd:
Un gioco poi è un'applicazione che è sottoposta ad una mole di input limitatissima, in quanto tutto può essere ridotto agli input filtrati della tastiera e del mouse... sicuramente prevedibilissimi, dato che se sono errati li si scarta e basta.
Questo per dire che non mi è mai capitato di aver trovato un bug che un test semplice da fare avrebbe potuto evitare; o quantomeno non ci ho mai messo di più a scovarlo che a scrivere un test...
Se poi contiamo che per seguire sta cosa avrei dovuto scrivere tests per parti che invece hanno funzionato alla prima, il bilancio-tempo era svantaggioso a dire poco.
Cmq sono sicuro che in un team a diversi livelli di skill, e con qualche "attrito" questa roba possa essere oro... io non mi fido di quello che scrivi, e allora te lo circondo di tests :asd:
solo che così più che curare si annulla il sintomo.
tomminno
13-11-2008, 08:09
Nemmeno se ti metti a lavorare ore con il debugger hai la garanzia che i sistemi esterni funzionino.
Con il debugger lo vedo all'istante se qualcuno mi ha modificato un Webservice o qualche cosa sul db, ottengo una bella eccezione. Se ho usato un mock continuerò ad avere il segnale verde che tutto funziona quando in realtà non è così.
Volendo anche la verifica della correttezza delle tabelle del DB è fattibile (quale problema c'è a farla ???). Si tratta solo di non mockare il DB e tenerlo accessibile durante tutte le operazioni.
Eh già il problema è creare sul db dei dati consistenti affinchè le operazioni che andrai ad eseguire nel software possano completare.
Perchè per creare i dati corretti devi implementare sul setup dei test tutto quello che viene svolto, magari da decine di software che girano precedentemente al tuo applicativo.
Ovvero per fare un test di un applicativo che richiede una settimana di sviluppo ti ci può volere anche più di un mese.
Credo che in ambito aziendale questa sia una condizione comune, visto che l'ho sempre riscontrato in tutte le aziende, ma non ho mai cercato di creare test automatici.
tomminno
13-11-2008, 08:10
Ma, forse non e poi una perdita di tempo se consideriamo che questa metodologia di sviluppo è stata usata con successo per esempio in contesti di sviluppo complessi in codice di dimensioni delle MLOC (vedi Fek, ad esempio, non penso che uno con la sua esperienza ci venga a raccontare frescacce. Poi ci sono anche i ragazzi di Diamond Crush che potrebbero avere qualcosa di interessante da dire in merito).
Forse dipende dal contesto, se hai un applicativo anche enorme ok, ma se hai un sistema complesso forse è difficile da mettere in pratica.
Con il debugger lo vedo all'istante se qualcuno mi ha modificato un Webservice o qualche cosa sul db, ottengo una bella eccezione. Se ho usato un mock continuerò ad avere il segnale verde che tutto funziona quando in realtà non è così.
Ripeto, puoi sempre simulare con un mock il caso in cui nella tabella siano presenti dati/attributi errati. Fai un test secondo cui il tuo codice debba generare un'eccezione con quel mock. Se non la genera test rosso. Se la genera test verde.
Hai testato la correttezza del tuo codice in reazione al variazioni dell'ambiente esterno non desiderate. Non puoi testare l'ambiente esterno.
Basta guardarlo dal giusto livello di astrazione.
Eh già il problema è creare sul db dei dati consistenti affinchè le operazioni che andrai ad eseguire nel software possano completare.
Perchè per creare i dati corretti devi implementare sul setup dei test tutto quello che viene svolto, magari da decine di software che girano precedentemente al tuo applicativo.
Ovvero per fare un test di un applicativo che richiede una settimana di sviluppo ti ci può volere anche più di un mese.
Io parlavo solo di testare la correttezza delle tabelle ;)
Niente vieta che la correttezza delle query venga testata indirettamente sulla correttezza di funzionamento delle classi del programma.
Forse dipende dal contesto, se hai un applicativo anche enorme ok, ma se hai un sistema complesso forse è difficile da mettere in pratica.
Ma questo è chiaro. Se hai un applicativo eterogeneo di sistemi e di linguaggio il TDD si deve limitare ad aiutarti a sviluppare la parte di codice per cui lo vuoi usare, non certo per il resto del sistema.
Come già detto da altri, ti indica solamente la correttezza del tuo modulo, non il funzionamento del sistema.
tomminno
13-11-2008, 10:17
Ripeto, puoi sempre simulare con un mock il caso in cui nella tabella siano presenti dati/attributi errati. Fai un test secondo cui il tuo codice debba generare un'eccezione con quel mock. Se non la genera test rosso. Se la genera test verde.
Non è esattamente questo lo scopo, non mi interessa (o meglio mi interessa relativamente) sapere se l'applicativo "sopravvive" all'eccezione, sono abbastanza confidente che lo faccia. Lo scopo è avere una segnalazione automatica che un applicativo non può più funzionare correttamente a seguito di una modifica che il più delle volte non è interna all'applicativo.
Hai testato la correttezza del tuo codice in reazione al variazioni dell'ambiente esterno non desiderate. Non puoi testare l'ambiente esterno.
Basta guardarlo dal giusto livello di astrazione.
Il problema è che la correttezza del mio codice dipende dall'ambiente esterno, se oggi è corretto domani potrebbe non esserlo più, ma a causa di modifiche all'ambiente non per modifiche interne al programma.
Non è esattamente questo lo scopo, non mi interessa (o meglio mi interessa relativamente) sapere se l'applicativo "sopravvive" all'eccezione, sono abbastanza confidente che lo faccia. Lo scopo è avere una segnalazione automatica che un applicativo non può più funzionare correttamente a seguito di una modifica che il più delle volte non è interna all'applicativo.
No, il mock non serve a sapere che il sistema sopravvive all'eccezione, ma mock e relativo test servono ad indurre nel sorgente la generazione di un'eccezione in quel particolare caso descritto dal mock e a testare se la generazione dell'eccezione avviene in maniera corretta se si presenta il problema.
Il problema è che la correttezza del mio codice dipende dall'ambiente esterno, se oggi è corretto domani potrebbe non esserlo più, ma a causa di modifiche all'ambiente non per modifiche interne al programma.
Stiamo parlando comunque di un sistema esterno mockato, giusto ?
Secondo me l'assunto errato che fai è quello in grassetto, il codice continua ad essere corretto per quanto viene descritto dai test !!! Il sorgente è corretto.
Esegui il codice e non funziona, hai l'eccezione ritornata, il punto in cui viene generata e questo fa capire dove si trova il problema. A questo punto sarà il programmatore a scrivere un nuovo test o a modificare un test esistente in modo da riflettere il cambiamento nel sistema esterno.
Il fatto di scrivere il codice in TDD non ti esime dal controllare se le cose funzionano tutte a runtime.
E' lo stesso discorso dei bug. Pari pari. I test in TDD continuano ad essere corretti ed il codice implementa perfettamente ciò che è descritto dai test. Trovato un bug sarò io a dover andare a scrivere un nuovo test che esercita il bug e che quindi fallisce. Dovrò agire di conseguenza e modificare il codice.
tomminno
13-11-2008, 11:01
No, il mock non serve a sapere che il sistema sopravvive all'eccezione, ma mock e relativo test servono ad indurre nel sorgente la generazione di un'eccezione in quel particolare caso descritto dal mock e a testare se la generazione dell'eccezione avviene in maniera corretta se si presenta il problema.
Si ok, con il mock testo il codice dell'applicativo, ma non è quello che mi interessa.
Stiamo parlando comunque di un sistema esterno mockato, giusto ?
Secondo me l'assunto errato che fai è quello in grassetto, il codice continua ad essere corretto per quanto viene descritto dai test !!! Il sorgente è corretto.
In realtà no, il codice era corretto, al passato.
Per fare un esempio stupido: software per il controllo di un automa per la guida automatica di una automobile: premi frizione, ingrana la prima, dai gas,...
Tutto funziona se la macchina ha il cambio manuale, se lo fai girare su una macchina col cambio automatico il programma magari "cerca" la frizione e non trovandola non va oltre, perfetto il programma funziona, ma non riesce a guidare la mia automobile e nessuno mi avvisa di questo. Quando mi accorgerò che il software non sta più funzionando come dovrebbe, ovvero guidare la macchina?
Con il test d'integrazione posso verificare al momento che cambio la macchina che qualcosa non funziona perchè avrò segnale rosso al test "PremiFrizione" che doveva essere invece superato.
Esegui il codice e non funziona, hai l'eccezione ritornata, il punto in cui viene generata e questo fa capire dove si trova il problema. A questo punto sarà il programmatore a scrivere un nuovo test o a modificare un test esistente in modo da riflettere il cambiamento nel sistema esterno.
Già ma se hai migliaia di software e non sai quale software ha smesso di funzionare (perchè magari lo ha loggato su un file di testo che nessuno guarderà mai) non puoi prendere provvedimenti. O meglio li prenderai solo quando te ne accorgi, magari qualche settimana dopo il danno.
Con i test potresti accorgerti dell'errore prima di pubblicare in produzione una modifica e prendere i provvedimenti adeguati prima di fare danni.
Il fatto di scrivere il codice in TDD non ti esime dal controllare se le cose funzionano tutte a runtime.
L'idea era quella di usare il TDD come controllore dell'integrazione, ma credo proprio che non sia sviluppo agile usato in questo senso.
Si ok, con il mock testo il codice dell'applicativo, ma non è quello che mi interessa.
Ma lo scopo dei test e è proprio quello di testare il codice del sistema su cui stai lavorando. Se sviluppi un gioco non devi metterti a testare il funzionamento di directx devi dare per scontato che funzioni. Sostanzialmente stai cercando di usare lo strumento sbagliato per questo nei riesci a capire come possa funzionare.
L'idea era quella di usare il TDD come controllore dell'integrazione, ma credo proprio che non sia sviluppo agile usato in questo senso.
Non è stato pensato per questo.
L'idea era quella di usare il TDD come controllore dell'integrazione, ma credo proprio che non sia sviluppo agile usato in questo senso.
Niente ti vieta di scrivere i test di integrazione prima del codice
- a parte una certa pigrizia nell'immaginare come si deve comportare il sistema.
In questo modo stai facendo TDD.
L'unico modo per vedere se il tutto funziona nel tuo contesto e' provare:
scrivi un test (che fallisce) di integrazione per una piccola (issima) funzionalita'
del tuo sistema, poi scrivi il codice (eventualmente TDD, con unit test scritti prima
delle unita' relative) che fa passare il test.
tomminno
13-11-2008, 13:24
Niente ti vieta di scrivere i test di integrazione prima del codice
- a parte una certa pigrizia nell'immaginare come si deve comportare il sistema.
In questo modo stai facendo TDD.
L'unico modo per vedere se il tutto funziona nel tuo contesto e' provare:
scrivi un test (che fallisce) di integrazione per una piccola (issima) funzionalita'
del tuo sistema, poi scrivi il codice (eventualmente TDD, con unit test scritti prima
delle unita' relative) che fa passare il test.
Il problema è creare i dati che fanno sì che il mio codice possa passare i test.
Nell'esempio stupido che avevo citato prima è come se per il test del mio sistema di guida automatico, dovessi costruirmi l'automobile perchè senza quella non posso testare il mio software.
Creare tutta l'automobile potrebbe essere (è) proibitivo.
banryu79
13-11-2008, 13:54
Ma, se non ho capito male il tuo esempio di prima, infatti tu stai testando l'automa.
L'automa deve conoscere, al minimo, l'interfaccia dell'automobile, non tutta l'automobile, poichè quello che ti interessa è testare il corretto utilizzo dell'interfaccia dell'automobile da parte dell'automa (l'automa deve conoscere l'interfaccia, sennò come fa a guidarla? Ovvero come si fa a determinare quali sono le sue specifiche?)
In quel caso, se non erro (perchè in effetti non ho esperienza diretta di TDD) il Mock ti serve per ricreare l'interfaccia da rende disponibile all'automa.
Non ti serve ricreare esattamente tutta l'automobile, perchè non è l'automobille che stai testando.
Spero di non aver fatto confusione :)
Ma, se non ho capito male il tuo esempio di prima, infatti tu stai testando l'automa.
L'automa deve conoscere, al minimo, l'interfaccia dell'automobile, non tutta l'automobile, poichè quello che ti interessa è testare il corretto utilizzo dell'interfaccia dell'automobile da parte dell'automa (l'automa deve conoscere l'interfaccia, sennò come fa a guidarla? Ovvero come si fa a determinare quali sono le sue specifiche?)
In quel caso, se non erro (perchè in effetti non ho esperienza diretta di TDD) il Mock ti serve per ricreare l'interfaccia da rende disponibile all'automa.
Non ti serve ricreare esattamente tutta l'automobile, perchè non è l'automobille che stai testando.
Esattamente.
tomminno
13-11-2008, 14:40
Ma, se non ho capito male il tuo esempio di prima, infatti tu stai testando l'automa.
L'automa deve conoscere, al minimo, l'interfaccia dell'automobile, non tutta l'automobile, poichè quello che ti interessa è testare il corretto utilizzo dell'interfaccia dell'automobile da parte dell'automa (l'automa deve conoscere l'interfaccia, sennò come fa a guidarla? Ovvero come si fa a determinare quali sono le sue specifiche?)
In quel caso, se non erro (perchè in effetti non ho esperienza diretta di TDD) il Mock ti serve per ricreare l'interfaccia da rende disponibile all'automa.
Non ti serve ricreare esattamente tutta l'automobile, perchè non è l'automobille che stai testando.
Spero di non aver fatto confusione :)
Si infatti con il mock aggiri il problema che io voglio diagnosticare.
Ovvero una segnalazione (un test) che qualcuno ha cambiato quell'interfaccia per cui il mio automa smette di funzionare come dovrebbe, perchè chiaramente impossibilitato a funzionare per cause esterne.
E' per questo che a me serve il test d'integrazione e non il mock. Il mock mi sarà utile per il test interno dell'applicativo.
banryu79
13-11-2008, 14:57
Ma scusa, qui c'è una confusione di responsabilità.
Cioè se è qualcun altro, "esterno", a fornire la "macchina" che il tuo "automa" deve pilotare, non può mica cambiartela da sotto i piedi (aggiornarla nei prodotti già distribuiti con una diversa) senza prima avvisarti.
Te lo deve dire, e ti deve fornire esplicitamente le specifiche della nuova interfaccia aggiornata (o le modifiche alla precedente).
Questo esula dagli scopi del TDD, secondo me (non so se ho capito il contesto in cui ti trovi ad operare).
@EDIT: Al limite il tuo "automa" deve essere dotato della capacità di comunicare con la "macchina", che so, tramite un metodo che le chieda di fornirgli l'interfaccia esposta, in modo che lui (l'automa) possa determinare se è compatibile con quella interfaccia o meno.
Chi ti fornisce la "macchina" deve dotarla della capacità di comunicare l'interfaccia che espone all'esterno.
E' di questo che si tratta?
Il mock mi sarà utile per il test interno dell'applicativo.
Nessuno ha mai detto il contrario.
tomminno
13-11-2008, 16:00
Ma scusa, qui c'è una confusione di responsabilità.
Cioè se è qualcun altro, "esterno", a fornire la "macchina" che il tuo "automa" deve pilotare, non può mica cambiartela da sotto i piedi (aggiornarla nei prodotti già distribuiti con una diversa) senza prima avvisarti.
In un sistema complesso può capitare benissimo che chi fa una modifca in una qualche parte del sistema non sappia minimamente chi sta usando quella risorsa e magari ne cambia l'interfaccia perchè crede di aver individuato tutti quelli che la usano e li ha modificati di conseguenza.
Te lo deve dire, e ti deve fornire esplicitamente le specifiche della nuova interfaccia aggiornata (o le modifiche alla precedente).
Ma se non sa a chi dirlo? Perchè non sa che anch'io lavoro su quella macchina?
Questo esula dagli scopi del TDD, secondo me (non so se ho capito il contesto in cui ti trovi ad operare).
Credo che il mio sia un semplice test d'integrazione che se non sbaglio è previsto dal TDD.
@EDIT: Al limite il tuo "automa" deve essere dotato della capacità di comunicare con la "macchina", che so, tramite un metodo che le chieda di fornirgli l'interfaccia esposta, in modo che lui (l'automa) possa determinare se è compatibile con quella interfaccia o meno.
Chi ti fornisce la "macchina" deve dotarla della capacità di comunicare l'interfaccia che espone all'esterno.
E' di questo che si tratta?
quello era solo un esempio.
Purtroppo il sistema è una spaghetti integration.
A me serve un sistema automatico di segnalazione che una modifica ha causato un malfunzionamento di qualche programma che prima funzionava correttamente e i test d'integrazione mi sembravano essere adatti.
banryu79
13-11-2008, 16:30
quello era solo un esempio.
Purtroppo il sistema è una spaghetti integration.
A me serve un sistema automatico di segnalazione che una modifica ha causato un malfunzionamento di qualche programma che prima funzionava correttamente e i test d'integrazione mi sembravano essere adatti.
Qui alzo le mani, non saprei cosa consigliare :fagiano:
una domanda, tra Jmock e EasyMock cosa scegliete?
Easymock mi sembra decisamente piu' amichevole.
Tornando in tema:
Ma se non sa a chi dirlo? Perchè non sa che anch'io lavoro su quella macchina?
Te lo dirà un test: quando fallirà saprai che è cambiata l'interfaccia.
Per l'esempio dell'automa, se hai un test che "esercita" il pedale della frizione
e il sistema passa al cambio automatico il test fallisce perché il pedale
della frizione non c'è...
Credo che il mio sia un semplice test d'integrazione che se non sbaglio è previsto dal TDD.
in genere per TDD si intende test di unità, comunque come ho detto sopra si può fare tdd anche con test di integrazione
quello era solo un esempio.
Purtroppo il sistema è una spaghetti integration.
Dovete cercare di ridurre al minimo il codice che interagisce con altri sistemi,
in modo da poter mockare (scusate il termine) tutto il possibile,
e compensare con un po' di test di integrazione.
A me serve un sistema automatico di segnalazione che una modifica ha causato un malfunzionamento di qualche programma che prima funzionava correttamente e i test d'integrazione mi sembravano essere adatti.
Quei test li puoi scrivere prima del codice relativo.
Nota a margine, dato che mi sembra in tema...l'opinione di Don Knuth sull'argomento, estratto da questa intervista http://www.informit.com/articles/article.aspx?p=1193856
Q: [...] today’s developers frequently build programs writing small code increments followed by immediate compilation and the creation and running of unit tests. What are your thoughts on this approach to software development?
A: [...] the idea of immediate compilation and "unit tests" appeals to me only rarely, when I’m feeling my way in a totally unknown environment and need feedback about what works and what doesn’t. Otherwise, lots of time is wasted on activities that I simply never need to perform or even think about. Nothing needs to be "mocked up."
banryu79
14-11-2008, 08:57
Sì beh, non è che ha offerto molti spunti di riflessione (intendo la frase estrapolata della risposta di Don Knuth, non il tuo intervento che invece reputo interessante).
Sono andato al link dell'intervista, ho letto la versione completa della domanda e della risposta, ma ancora non ho trovato argomentazioni e/o spunti utili pro o contro il TDD.
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.