Introduzione al Test Driven Development
-
Upload
ennio-masi -
Category
Technology
-
view
6.147 -
download
0
description
Transcript of Introduzione al Test Driven Development
TEST-DRIVEN DEVELOPMENT
Ennio Masi – 961/85
Prof. Sergio Di Martino
Ingegneria del software 2 – 2007/08
Sommario
• Cos’è il TDD?• Il TDD e l’XP• Unit Testing e xUnit
Framework• TDD Mantra• I princìpi• TDD Patterns
• Legacy code• I tipi di test• Benefici e limiti
Cos’è?
• Test-Driven Development
• Test-First Programming
• Test-Driven Design
Cos’è? (2)
• E’ una tecnica iterativa di sviluppo del software.
• L’obiettivo del TDD è il design del software, non la sua validazione.
TDD e l’XP
• Il TDD è una pratica agile.
• L’XP è una metodologia agile.
TDD e l’XP (2)
• Il TDD nasce dall’estrazione di due concetti fondamentali dell’XP:– Test First Programming– Refactoring
TDD e l’XP (3)
• Il TDD è il cuore dell’Extreme Programming.
• TDD può essere applicato senza l’aggiunta delle altre pratiche dell’XP.
• L’XP segna una strada da seguire per essere preparati ai cambiamenti futuri.
• Il TDD cerca di controllare il gap tra le decisioni ed i feedback durante lo sviluppo.
Obiettivi del TDD
• Rendere lo sviluppo migliore e più rapido.
• Mantenere il codice sempre documentato.
• Ottenere codice flessibile e facilmente estendibile.
• Diminuire la presenza di bug nel codice di produzione.
Unit Testing e xUnit Framework
Unit Test
• Un test non è di unità se:– comunica con il database– comunica in rete– modifica il database– non può essere lanciato in parallelo ad altri
test– bisogna effettuare diverse operazioni per
lanciare il test
“Unit tests run fast. If they don’t run fast, they aren’t unit tests.”
Unit Test ed il TDD
• Sono rilasciati con il codice di produzione.
• Nel TDD una funzionalità è rilasciata soltanto se vi è associata almeno uno Unit Test.
• Consentono di effettuare il refactoring di una funzionalità senza “paura”.
• Meno debugging.
I Mock Objects
• Un mock object è una simulazione di un oggetto reale.
• Implementa l’interfaccia dell’oggetto da simulare ed ha il suo stesso comportamento.
• Forniscono una risposta pre-impostata.• Servono per capire se l’oggetto che li usa lo fa
correttamente.• Utilissimi per testare unità senza legarsi ad
oggetti esterni.
I Mock Objects (2)
• Per superare una validazione utilizzando i mock, gli oggetti che li usano devono chiamare il metodo corretto con i parametri giusti e nell’ordine che ci aspettiamo. (mock’s validation)
• Un oggetto che semplicemente simula un oggetto reale, senza effettuare la verifica detta al punto precedente non è un mock, è uno stub.
• Esistono implementazioni dei Mock Objects per moltissimi linguaggi. ( http://www.mockobjects.com )
Esempio
• Utilizzare un mock in questo caso consente di effettuare il testing senza l’ausilio del vero database di produzione.
• Il mock deve avere la stessa interfaccia dell’oggetto che implementa il database e lo stesso comportamento dalla prospettiva del software client.
L’esempio classico è quello di implementareun mock object che simuli la connessione ad un database:
Esempio (2)DBConnection.java
public interface DBConnection{ void connect(); void close();}
public class MockDBConnection implements DBConnection{ private boolean connected = false; private boolean closed = false; public void connect() {connected = true;} public void close() {closed = true;} public boolean validate(){return connected && closed;}}
MockDBConnection.java
xUnit Framework
• Ha diversi utilizzi– Design del software– Implementazione del codice– Debugging– Ottimizzazione delle performance– Quality Assurance (QA)
E’ un tool software che consente di scriveree lanciare unit test.
E’ il core del TDD
xUnit Framework (2)
• JUnit• CppUnit• Nunit• PyUnit• vbUnit• …
Nel 1999 Kent Beck presentò uno unittest framework per il linguaggio Smalltalk (SUnit)Da quel framework sono stati effettuati moltiporting:
L’architettura
Assert
Dal seminario su JUnit:
“Gli Assert sono metodi che consentonodi asserire che una certa condizione è
vera o falsa.”
Assert (2)
• assertTrue([message], condition) : Il test è superato se la condizione è vera
• assertFalse([message], condition) : Il test è superato se la condizione è falsa
• assertNull([message], Object) : Il test è superato se il riferimento all’oggetto è NULL.
• assertNotNull([message], Object) : il test è superato se il riferimento all’oggetto non è NULL.
• assertEquals([message], value1, value2) : il test è superato se value1 == value2.
• assertNotEquals([message], value1, value2) : il test è superato se non vale che value1 == value2
• fail() : wrapper di assertTrue(false)
Assert (3)
• (Teoria) Una regola generale è che ogni test deve contenere soltanto un assert.
• (Pratica) Spesso i metodi di test contengono più assert oppure assert singoli ma composti da più condizioni legate da operatori logici.
Testing degli errori
public void testXXX(){ try { //an operation fail(“eccezione non avvenuta”) } catch (Exception e) {} ...}
• Questo test genera un’eccezione e controlla che sia gestito come ci aspettiamo.
• Nell’esempio ci aspettiamo che sia lanciata l’eccezione nel momento i cui è chiamata anOperation.
• Il test fallisce se l’eccezione non avviene, mentre va a buon fine se l’eccezione viene lanciata.
• fail è equivalente ad assertTrue(false) ma è più leggibile.
E’ fondamentale testare la gestione degli errori.Viene utilizzato il metodo fail.
public void testXXX(){ try { //an operation } catch (Exception e) { fail(e.getMessage()); } ...}
Testing degli errori (2)E’ fondamentale testare la gestione degli errori.
Viene utilizzato il metodo fail.• Questo test genera un’eccezione
e controlla che sia gestito come ci aspettiamo.
• In questo esempio ci aspettiamo che non sia lanciata l’eccezione nel momento in cui è chiamata anOperation.
• Il test fallisce se l’eccezione avviene, mentre va a buon fine se l’eccezione non viene lanciata.
• fail è equivalente ad assertTrue(false) ma è più leggibile.
Il processo di sviluppo:TDD Mantra
Le regole d’oro del TDD
• Si scrive nuovo codice solo se un test automatico è fallito.
• Eliminare i duplicati.
Test-First Programming + Refactoring
Implicazioni tecniche
• Ogni programmatore scrive i propri test.
• Fornire risposte rapide ai piccoli cambiamenti.
• Codice con alta coesione e basso accoppiamento, altrimenti risulta difficile fare i test.
TDD MantraPrimo step
Think
Think : ragionare per piccoli passi
Focalizzarsi sul comportamento che il codicedeve avere.
TDD Mantra
Think
“Vogliamo sviluppare una libreria aritmeticache agisca solo su numeri non negativi”
Esempio
aritLib.py
Think
TDD MantraEsempio
Ragioniamo sulla somma:• Se in input vi sono due numeri non negativi, ritorniamo
la somma.• Se almeno uno dei due input è negativo allora ritorna -1
TDD MantraSecondo step
Think
Red Bar : scrittura dei test.
Focalizzarsi sul comportamento della classee la sua interfaccia pubblica.
Red bar
TDD MantraSecondo step
Think
import aritLibimport unittest
class AritLibTest(unittest.TestCase): knownValues = ((0, 0, 0), (1, 1, 2), (2, 3, 5), (-1, -1, -1), (-10, 10, -1),(10, -5, -1), ) def testAdd(self): for x, y, sum in self.knownValues: result = aritLib.add(x, y) self.assertEquals(sum, result)
Red bar
TDD MantraSecondo step
Think
ERROR: testAdd (__main__.AritLibTest)----------------------------------------------------------------------Traceback (most recent call last):File “AritLibTest.py”, line 11, in testAddresult = aritLib.add(x,y)AttributeError: 'module' object has no attribute 'add'----------------------------------------------------------------------Ran 1 test in 0.000sFAILED (errors=1)
Red bar
class AritLibTest(unittest.TestCase): knownValues = ((0, 0, 0), (1, 1, 2), (2, 3, 5), (-1, -1, -1), (-10, 10, -1),(10, -5, -1), ) def testAdd(self): for x, y, sum in self.knownValues: result = aritLib.add(x, y) self.assertEquals(sum, result)
IL TEST FALLISCE PERCHE’ LA FUNZIONEANCORA NON ESISTE!
TDD MantraTerzo step
Think Red bar Green Bar
Test fallito
Green Bar : scrittura del codice.
Scrivere solo il codice necessario per superarei test scritti in precedenza.
TDD MantraTerzo step
Think Red bar Green Bar
Test fallito
aritLib.py
def add(x, y): if x < 0: return -1 if y < 0: return -1 return x+y
TDD MantraTerzo step
Think Red bar Green Bar
Test fallito
aritLib.py
def add(x, y): if x < 0: return -1 if y < 0: return -1 return x+y
----------------------------------------------------------------------Ran 1 test in 0.000 s----------------------------------------------------------------------OK
Vengono lanciati i test scritti in precedenza senzamodificarli.
TDD MantraQuarto step
Think Red bar Green Bar
Test fallito
Refactoring
Refactoring : semplificare ed aggiustare il desingdella funzionalità sviluppata.
In questa fase non bisogna assolutamente modificarela semantica della funzionalità implementata!
TDD MantraQuarto step
Think Red bar Green Bar
Test fallito
Prima
def add(x, y): if x < 0: return -1 if y < 0: return -1 return x+y
Refactoring
Dopo
def add(x, y): if x < 0 or y < 0: return -1 return x+y
TDD MantraQuarto step
Think Red bar Green Bar
Test fallito
Refactoring
Dopo
def add(x, y): if x < 0 or y < 0: return -1 return x+y
----------------------------------------------------------------------Ran 1 test in 0.000 s----------------------------------------------------------------------OK
Vengono lanciati i test scritti in precedenza senzamodificarli.
I princìpi
Think Red bar Green Bar
Test fallito
Refactoring
•“Code once, test twice”•“Clean code that works”•KISS: Keep It Short & Simple•YAGNI: You Ain’t Gonna Need It
Quando fermarsi?
• Quando il sistema effettivamente funziona.
• Il sistema ha superato il 100% dei test.
• Non c’è codice duplicato.
• Il sistema dovrebbe avere meno classi e metodi possibile.
Bad smells …
• C’è qualcosa che non va se …– E’ necessario testare metodi privati/protetti.– Necessitiamo di testing White Box.– E’ necessario configurare il sistema prima di
lanciare i test.– I test funzionano ad intermittenza.– I test sono molto lenti.
Implicazioni sociali
• Il TDD consente di “gestire” le paure durante lo sviluppo.
• La paura ha molti aspetti negativi durante lo sviluppo:– rende incerti– toglie la voglia di comunicare– rende timorosi davanti ai feedback– rende nervosi
Implicazioni sociali (2)
• Il TDD consente di gestire le “paure” durante lo sviluppo:– si procede al rilascio del codice solo se ha
superato il 100% dei test previsti.– Il design va sempre di pari passo con lo
sviluppo.– Il TDD consente ai programmatori di
conoscere perfettamente il codice.
La velocità
• Nel TDD è importante la velocità con la quale i test sono eseguiti.
• Se i test non fossero veloci allora sarebbero una distrazione.
• Se i test non fossero veloci allora non sarebbero lanciati con alta frequenza: che vantaggio avrebbe il TDD?
“Unit tests run fast. If they don’t run fast, they aren’t unit tests.”
Le linee guida
TDD Patterns
• Red Bar patterns:– Comincia da un test semplice.– Se hai un’idea nuova aggiungi il test alla lista
ma rimani su quello che stai facendo.– Aggiungi un test per ogni difetto trovato.– Se non riesci ad andare avanti butta via tutto
e riscrivilo.
TDD Patterns
• Testing patterns:– Se il test richiede troppo tempo per funzionare
allora dividilo in parti più semplici.– Se i test hanno bisogno di oggetti complessi
allora usa i mock objects.– Mantieni il log dell’esecuzione dei test.– Se lavori solo lascia rotto l’ultimo test della
giornata.– Se lavori in team lascia sempre i test
funzionanti.
TDD Patterns
• Green Bar patterns:– Scrivi il codice più semplice per passare il
test.– Scrivi l’implementazione più ovvia per
superare il/i test corrente/i.– Se un’operazione lavora su collezioni allora
scrivi l’implementazione prima sul singolo oggetto e dopo generalizza.
Focused integrationtesting ed End-to-End
testing
Focused integration testing
• Un focused integration test è focalizzato a testare:– la comunicazione con il database– la comunicazione in rete– la comunicazione con il filesystem– la comunicazione con oggetti esterni
Gli unit test da soli non bastano, il codicedeve comunicare con l’esterno.
Focused integration testing (2)
Se necessitiamo di molti integration tests allora c’è qualcosa che non va.
Es. Se tutte i business object parlanodirettamente con il database allora il codicenon ha un buon design!
Il codice che parla con l’esterno non è nében coeso nè ben accoppiato.
Focused integration testing (2)
Se necessitiamo di molti integration tests allora c’è qualcosa che non va.
Es. Se tutte i business object parlanodirettamente con il database allora il codicenon ha un buon design!
Il codice che parla con l’esterno non è nében coeso nè ben accoppiato.
Un miglior design prevede che vi sia un’unica classe che parli con il database.
End-to-end Testing
• Serve per testare l’intero sistema:– Test di intere storie di utilizzo del sistema,
dalla GUI utente all’utilizzo del database …
• Contro:– Difficile da portare a termine.– Difficile da settare.– Difficile individuare gli errori.– Molto lento.– Non automatizzabile.
La velocità di esecuzione dei test
• Unit tests : un centinaio al secondo.
• Focused integration tests : una decina al secondo.
• End-to-end tests : diversi secondi per ogni singol test.
Il TDD ed il Legacy Code
Legacy Code
• Problemi:– Mancanza di documentazione– Difficile da capire in profondità– Non è progettato pensando alla “testabilità”
“Legacy code is code without tests”
Legacy Code (2)
I passi per affrontare il legacy code:
• Inizia a scrivere test per capire se il legacy code (una sua parte) è stato ben compreso.
• Adatta il test finchè non funziona bene.
Legacy Code (3)
• Problemi:– Quanto codice è stato testato ?– Quali aree necessitano di testing ?– Quali sono i rischi del codice?
Difficile avere una visione d’insieme del codice
Benefici del TDD
• Si mantiene il codice semplice
• Sviluppo rapido
• I test sono sia design che documentazione
• Facile comprensione del codice
• Bug trovati presto nello sviluppo
• Meno debugging
• Basso costo dei cambiamenti
Limiti del TDD
• Curva di apprendimento alta• I manager sono restii ad applicarlo• Richiede una grande disciplina• Difficile applicarlo alle GUI• Di difficile applicazione a Legacy Code
Riferimenti
• Test-Driven Development: By Examples Kent Beck 2002
• Test-Driven Development: A pratical guide Astels
• Unit Test Frameworks Paul Hamill