Introduzione al Test Driven Development

61
TEST-DRIVEN DEVELOPMENT Ennio Masi – 961/85 Prof. Sergio Di Martino Ingegneria del software 2 – 2007/08

description

Seminario presso l'Università degli studi di Napoli Federico II. Introduzione al Test Driven Development

Transcript of Introduzione al Test Driven Development

Page 1: Introduzione al Test Driven Development

TEST-DRIVEN DEVELOPMENT

Ennio Masi – 961/85

Prof. Sergio Di Martino

Ingegneria del software 2 – 2007/08

Page 2: Introduzione al Test Driven Development

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

Page 3: Introduzione al Test Driven Development

Cos’è?

• Test-Driven Development

• Test-First Programming

• Test-Driven Design

Page 4: Introduzione al Test Driven Development

Cos’è? (2)

• E’ una tecnica iterativa di sviluppo del software.

• L’obiettivo del TDD è il design del software, non la sua validazione.

Page 5: Introduzione al Test Driven Development

TDD e l’XP

• Il TDD è una pratica agile.

• L’XP è una metodologia agile.

Page 6: Introduzione al Test Driven Development

TDD e l’XP (2)

• Il TDD nasce dall’estrazione di due concetti fondamentali dell’XP:– Test First Programming– Refactoring

Page 7: Introduzione al Test Driven Development

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.

Page 8: Introduzione al Test Driven Development

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.

Page 9: Introduzione al Test Driven Development

Unit Testing e xUnit Framework

Page 10: Introduzione al Test Driven Development

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.”

Page 11: Introduzione al Test Driven Development

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.

Page 12: Introduzione al Test Driven Development

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.

Page 13: Introduzione al Test Driven Development

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 )

Page 14: Introduzione al Test Driven Development

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:

Page 15: Introduzione al Test Driven Development

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

Page 16: Introduzione al Test Driven Development

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

Page 17: Introduzione al Test Driven Development

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:

Page 18: Introduzione al Test Driven Development

L’architettura

Page 19: Introduzione al Test Driven Development

Assert

Dal seminario su JUnit:

“Gli Assert sono metodi che consentonodi asserire che una certa condizione è

vera o falsa.”

Page 20: Introduzione al Test Driven Development

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)

Page 21: Introduzione al Test Driven Development

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.

Page 22: Introduzione al Test Driven Development

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.

Page 23: Introduzione al Test Driven Development

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.

Page 24: Introduzione al Test Driven Development

Il processo di sviluppo:TDD Mantra

Page 25: Introduzione al Test Driven Development

Le regole d’oro del TDD

• Si scrive nuovo codice solo se un test automatico è fallito.

• Eliminare i duplicati.

Test-First Programming + Refactoring

Page 26: Introduzione al Test Driven Development

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.

Page 27: Introduzione al Test Driven Development

TDD MantraPrimo step

Think

Think : ragionare per piccoli passi

Focalizzarsi sul comportamento che il codicedeve avere.

Page 28: Introduzione al Test Driven Development

TDD Mantra

Think

“Vogliamo sviluppare una libreria aritmeticache agisca solo su numeri non negativi”

Esempio

aritLib.py

Page 29: Introduzione al Test Driven Development

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

Page 30: Introduzione al Test Driven Development

TDD MantraSecondo step

Think

Red Bar : scrittura dei test.

Focalizzarsi sul comportamento della classee la sua interfaccia pubblica.

Red bar

Page 31: Introduzione al Test Driven Development

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

Page 32: Introduzione al Test Driven Development

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!

Page 33: Introduzione al Test Driven Development

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.

Page 34: Introduzione al Test Driven Development

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

Page 35: Introduzione al Test Driven Development

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.

Page 36: Introduzione al Test Driven Development

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!

Page 37: Introduzione al Test Driven Development

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

Page 38: Introduzione al Test Driven Development

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.

Page 39: Introduzione al Test Driven Development

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

Page 40: Introduzione al Test Driven Development

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.

Page 41: Introduzione al Test Driven Development

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.

Page 42: Introduzione al Test Driven Development

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

Page 43: Introduzione al Test Driven Development

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.

Page 44: Introduzione al Test Driven Development

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.”

Page 45: Introduzione al Test Driven Development

Le linee guida

Page 46: Introduzione al Test Driven Development

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.

Page 47: Introduzione al Test Driven Development

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.

Page 48: Introduzione al Test Driven Development

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.

Page 49: Introduzione al Test Driven Development

Focused integrationtesting ed End-to-End

testing

Page 50: Introduzione al Test Driven Development

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.

Page 51: Introduzione al Test Driven Development

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.

Page 52: Introduzione al Test Driven Development

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.

Page 53: Introduzione al Test Driven Development

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.

Page 54: Introduzione al Test Driven Development

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.

Page 55: Introduzione al Test Driven Development

Il TDD ed il Legacy Code

Page 56: Introduzione al Test Driven Development

Legacy Code

• Problemi:– Mancanza di documentazione– Difficile da capire in profondità– Non è progettato pensando alla “testabilità”

“Legacy code is code without tests”

Page 57: Introduzione al Test Driven Development

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.

Page 58: Introduzione al Test Driven Development

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

Page 59: Introduzione al Test Driven Development

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

Page 60: Introduzione al Test Driven Development

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

Page 61: Introduzione al Test Driven Development

Riferimenti

• Test-Driven Development: By Examples Kent Beck 2002

• Test-Driven Development: A pratical guide Astels

• Unit Test Frameworks Paul Hamill