Lezione 3: Sviluppo in Extreme Programming

1,684 views

Published on

✦ Progettazione in XP
✦ Principi di progettazione: Semplicità
✦ Test Driven Development
✦ Self Documenting Code
✦ Once and Only Once
✦ You Ain’t Gonna Need It
✦ Automazione dei test in Java: JUnit

Published in: Education, Technology
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,684
On SlideShare
0
From Embeds
0
Number of Embeds
24
Actions
Shares
0
Downloads
0
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

Lezione 3: Sviluppo in Extreme Programming

  1. 1. Lezione 19: Sviluppo in Extreme Programming Corso di Ingegneria del Software Laurea Magistrale in Ing. Informatica Università degli Studi di Salerno 1
  2. 2. Outline ✦ Progettazione in XP ✦ Principi di progettazione: Semplicità ✦ Test Driven Development ✦ Self Documenting Code ✦ Once and Only Once ✦ You Ain’t Gonna Need It ✦ Automazione dei test in Java: JUnit 2
  3. 3. Progettazione in XP ✦ Anche la progettazione viene effettuata in maniera incrementale ✦ Non c’è una singola figura responsabile della progettazione • tutto il team condivide la responsabilità di progettazione ✦ Non viene prodotta una documentazione formale della progettazione, specialmente per la progettazione di dettaglio 3
  4. 4. CRC Cards ✦ Lo strumento principale usato durante la discussione delle scelte progettuali sono le schede Class-Responsibility- Collaboration (CRC Cards) ✦ Ogni scheda (tipicamente scritta a mano durante la discussione) indica: • il nome di una classe • le responsabilità della classe • le altre classi con cui essa interagisce 4
  5. 5. CRC Cards 5
  6. 6. CRC Cards ✦ Le schede CRC non fanno parte della documentazione mantenuta e tracciata nel progetto ✦ Sono utilizzate esclusivamente come ausilio durante la discussione, e come promemoria delle decisioni prese 6
  7. 7. The Source Code is the Design ✦ In XP la descrizione ufficiale delle scelte progettuali non è in un documento formale, ma è il codice sorgente stesso (inclusi i commenti)! • lavorando su piccoli incrementi, non è necessario avere una documentazione formale dettagliata per guidare lo sviluppo • se necessario è possibile “estrarre” con strumenti automatici una rappresentazione più astratta del design dal codice sorgente (es. diagrammi UML); in ogni caso è il codice il riferimento autoritativo 7
  8. 8. The System Metaphor ✦ Anche se non si documentano le decisioni progettuali di basso livello, in genere è necessaria una descrizione dell’architettua ✦ A questo scopo si usa la System Metaphor: • documento informale e conciso • descrive in termini di alto livello la struttura del sistema e i concetti fondamentali, spesso attraverso analogie • ha lo scopo di facilitare la comunicazione tra gli sviluppatori e di stabilire un linguaggio comune 8
  9. 9. Progettazione in XP ✦ La linea guida fondamentale: Do the simplest thing that possibly work “Tutto ciò che è complesso non è utile. Tutto ciò che è utile è semplice.” M. Kalashnikov 9
  10. 10. The simplest thing... ✦ La “cosa più semplice” può avere diversi significati: • la prima cosa che viene in mente • la cosa che si realizza con il minore sforzo • la cosa che può capire anche chi non ha molta conoscenza/competenza • ... ✦ Nessuno di questi significati corrisponde al concetto di semplicità richiesto da XP 10
  11. 11. The simplest thing... ✦ Il codice più semplice per risolvere un problema è quello che: • supera tutti i test • esprime ogni idea che gli sviluppatori intendevano esprimere • contiene ogni concetto una sola volta • non ha parti superflue 11
  12. 12. Test Driven Development ✦ Il codice che risolve un problema deve superare tutti i test ✦ Le metodologie tradizionali posizionano il testing alla fine del ciclo di sviluppo, quando il codice è completo ✦ XP adotta un approccio diametralmente opposto: Test Driven Development (TDD) 12
  13. 13. Test Driven Development ✦ Il TDD si può riassumere nelle seguenti raccomandazioni: • Test Early • Test Often • Test Automatically 13
  14. 14. Test Early ✦ Nell’aggiungere una nuova funzione al programma, lo sviluppatore: • innanzitutto scrive l’interfaccia con un’implementazione vuota • quindi scrive il codice per testare la nuova funzione (unit tests) • implementa la funzione • verifica che l’implementazione supera il test • verifica che tutte le altre funzioni precedentemente implementate superino i loro test • solo a questo punto il lavoro sulla funzione è 14 completo
  15. 15. Test Early ✦ Benefici: • lo sviluppatore verifica l’usabilità dell’interfaccia della funzione • lo sviluppatore verifica la sua comprensione dei requisiti • il testing non è relegato alla fine del progetto, quando la prossimità della deadline può spingere a farlo in maniera frettolosa e approssimativa • la necessità di testare una singola funzione per volta spinge lo sviluppatore a progettare codice con basso accoppiamento 15
  16. 16. Test Often ✦ Il codice dei test non è eseguito una volta sola: • ad ogni modifica del software vengono eseguiti i test non solo della parte modificata ma anche di tutte le altre parti del software • il codice sviluppato viene integrato nella versione corrente del software solo se supera tutti i test 16
  17. 17. Test Often ✦ Benefici: • i bug vengono individuati presto nel ciclo di sviluppo, quando è più semplice circoscriverne la causa • gli sviluppatori possono modificare con più tranquillità il codice esistente, sapendo che se dovessero introdurre dei bug essi sarebbero subito rilevati • il numero di bug che finiscono nella versione rilasciata del software è significativamente minore • si evita la “regressione” del software (si parla anche di “test di regressione”) 17
  18. 18. Test Automatically ✦ I test devono essere eseguibili automaticamente (senza intervento dell’utente) ✦ Un solo comando deve consentire di lanciare tutti i test del progetto ✦ Lo sviluppatore deve avere i risultati dei test in forma sintetica (es. quanti sono i test falliti) con la possibilità, se ne ha bisogno, di ottenerli in forma analitica (quali sono i test falliti) 18
  19. 19. Test Automatically ✦ Benefici: • gli sviluppatori sono incoraggiati a eseguire i test spesso, dal momento che devono lanciare un singolo comando • è possibile usare programmi che lanciano i test periodicamente (es. ogni notte) e avvisano i responsabili del progetto dei risultati • poiché non è possibile automatizzare il testing dell’interfaccia utente (se non in maniera limitata), gli sviluppatori sono incoraggiati a separare l’interfaccia utente dalla business logic 19
  20. 20. TDD e Refactoring ✦ Cambiamenti di implementazione ma non di interfaccia: • i test già esistenti consentono di verificare che la nuova implementazione è compatibile con la vecchia ✦ Cambiamenti di interfaccia a livello sintattico: • occorre modificare tutti i test della funzione ✦ Cambiamenti di interfaccia a livello semantico: • occorre esaminare tutti i test della funzione per verificare che siano coerenti con la nuova 20 semantica
  21. 21. TDD e debugging ✦ Quando si scopre un bug non rilevato dai test: • innanzitutto si crea un nuovo test che fallisca a causa del bug • solo a questo punto si provvede a eliminare il bug ✦ Benefici: • un bug eliminato una volta non rischia di essere reintrodotto in una successiva modifica del codice 21
  22. 22. Self Documenting Code ✦ Il codice più semplice deve esprimere ogni idea che gli sviluppatori intendevano esprimere • non mettere l’implementazione di idee non correlate nella stessa funzione/classe/metodo (Alta coesione) • l’organizzazione delle unità del programma dovrebbe rendere comprensibile l’organizzazione delle idee • i nomi delle entità del programma dovrebbero rendere chiaro il loro significato minimizzando la documentazione necessaria 22
  23. 23. Self Documenting Code ✦ Principio della Minima Sorpresa: tra le possibili interpretazioni di un frammento di programma, quella corretta dovrebbe essere quella più ovvia per lo sviluppatore • convenzioni di codifica comuni • convenzioni comuni per la scelta dei nomi ‣ la difficoltà nella scelta del nome di una classe, funzione o altra entità, spesso è indice di una cattiva progettazione: l’entità in questione non ha una singola responsabilità ben definita 23
  24. 24. Self Documenting Code ✦ Benefici • riduzione della necessità di documentazione esplicita ‣ uso di strumenti di documentazione automatica come JavaDoc ‣ eventuali altri tipi di documentazione sono prodotti solo su richiesta dell’utente • maggiore semplicità di manutenzione, anche per sviluppatori diversi dal team iniziale 24
  25. 25. Once and Only Once ✦ Ogni informazione deve avere una rappresentazione unica, non ambigua e autoritativa all’interno del sistema • in letteratura si parla anche di “principio DRY” (Don’t Repeat Yourself) • vale sia per i dati che per il codice del sistema • non esclude la presenza di copie “meccaniche” delle informazioni (es. cache), ma deve essere unica la rappresentazione a cui gli sviluppatori fanno riferimento 25
  26. 26. Once and Only Once ✦ Benefici: • è minore il rischio di introdurre incongruenze • è più facile modificare il sistema 26
  27. 27. Once and Only Once ✦ In generale non è banale applicare nel modo migliore questo principio: • non sempre è facile riconoscere che due parti del programma fanno qualcosa di simile • spesso la duplicazione si manifesta solo astraendo l’informazione rispetto a un insieme di parametri ✦ Tuttavia ci sono alcune violazioni grossolane facili da individuare: • Cut and Paste programming • costanti letterali nel codice • ... 27
  28. 28. Once and Only Once ✦ Questo principio rappresenta la principale forza che guida il Refactoring di un programma: • periodicamente, dopo l’aggiunta di una o più funzioni, gli sviluppatori esaminano il programma per individuare nuove opportunità di rimuovere duplicazioni • in questo modo si evita che la struttura del programma possa “andare alla deriva” durante il ciclo di sviluppo 28
  29. 29. Parti superflue ✦ Il programma non deve contenere parti superflue • occorre rimuovere le parti che non sono più necessarie • occorre evitare di introdurre nuove funzioni che non sono ancora necessarie 29
  30. 30. Parti superflue ✦ Benefici: • le parti superflue rendono più complicata la struttura del programma, e aumentano le sue dimensioni • le parti superflue possono contribuire all’introduzione di bug (es. ARIANE 5) 30
  31. 31. Necessità future ✦ Spesso gli sviluppatori hanno la tentazione di aggiungere nuove funzioni che non sono attualmente necessarie • previsione che queste funzioni possano essere utili in futuro • previsione che il costo per implementare queste funzioni sia trascurabile • queste funzioni sono “interessanti da realizzare” ✦ Questa tendenza può manifestarsi anche rendendo più generale del necessario una funzione 31
  32. 32. You Ain’t Gonna Need It ✦ In XP si raccomanda di contrastare questa tendenza: • gli sviluppatori devono lavorare sui requisiti noti oggi, non su ipotesi infondate su quelli futuri • le esigenze future potrebbero essere diverse da quelle previste • cercare di anticipare le esigenze future introduce parti superflue nel programma ✦ Convenzionalmente si usa la frase “You Ain’t Gonna Need It” (abbreviata in YAGNI) per indicare questa raccomandazione 32
  33. 33. Automazione dei test in Java ✦ Abbiamo visto che il Test Driven Development richiede l’automatizzazione degli Unit Test ✦ Il modo con cui è possibile automatizzare i test dipende dal linguaggio e dall’ambiente di sviluppo ✦ Per il linguaggio Java lo standard de facto è la libreria JUnit 33
  34. 34. JUnit ✦ Creata da K. Beck e E. Gamma come porting al linguaggio Java del framework SUnit, sviluppato da Beck per il linguaggio Smalltalk ✦ “Pure Java”, portabile su qualsiasi piattaforma ✦ Utilizzabile attraverso la command line, ma anche supportata direttamente da ambienti di sviluppo come Eclipse 34
  35. 35. JUnit ✦ Sito: http://junit.org ✦ Nota: • faremo riferimento alla versione 4.xx di JUnit, che richiede il JDK 1.5 o successivi • per lavorare con JDK precedenti a 1.5 occorre la versione 3.xx di JUnit, meno semplice da usare 35
  36. 36. Installazione di JUnit ✦ Dobbiamo ottenere il file junit-4.xx.jar dal sito (ad es. la versione corrente è junit-4.5.jar) • NOTA: la versione corrente di Eclipse include tra i plugin di default JUnit 4.3 ✦ Il file .jar deve essere inserito nel classpath di Java sia durante la compilazione che durante l’esecuzione dei test 36
  37. 37. Uso di JUnit ✦ Tipicamente, per ogni classe da testare si crea una classe di test; per convenzione il nome della classe di test si ottiene aggiungendo “Test” al nome della classe da testare • Esempio: se abbiamo una classe Adder, gli unit test di questa classe saranno nella classe AdderTest 37
  38. 38. Uso di JUnit ✦ Note: • la classe di test deve essere dichiarata public • se la classe da testare ha metodi protected o visibili a livello di package, la classe di test deve trovarsi nello stesso package ✦ Nel file .java della classe di test occorre inserire le seguenti direttive: import org.junit.*; import static org.junit.Assert.*; 38
  39. 39. Uso di JUnit: metodi di test ✦ Nella classe di test, occorre inserire dei metodi per eseguire i test veri e propri ✦ Per convenzione, il nome di questi metodi è testNomeMetodo, dove nomeMetodo è il nome del metodo da testare ✦ I metodi di test devono essere dichiarati come: @Test public void testAdd() { // implementazione del test // per il metodo ‘add’ ....... } 39
  40. 40. Uso di JUnit: metodi di test ✦ Nota: @Test è una annotazione che segnala a JUnit che il metodo seguente è un metodo di test; la classe di test potrebbe contenere altri metodi ausiliari che non devono essere eseguiti direttamente da JUnit 40
  41. 41. Uso di JUnit: asserzioni ✦ All’interno dei metodi di test si richiamano dei metodi il cui nome è assertCondizione per specificare le condizioni che devono essere verificate ✦ Al momento dell’esecuzione, un test si considera superato se tutte le condizioni specificate risultano verificate 41
  42. 42. Uso di JUnit: asserzioni ✦ Principali asserzioni di JUnit: • assertEquals(expected, actual) assertArrayEquals(expected, actual) ‣ richiede che il valore ‘expected’ sia uguale a ‘actual’ • assertTrue(cond) ‣ richiede che `cond’ abbia come valore: `true’ • assertFalse(cond) ‣ richiede che `cond’ abbia come valore `false’ • assertNull(obj) ‣ richiede che `obj’ sia un riferimento nullo • assertNotNull(obj) ‣ richiede che `obj’ sia un riferimento non nullo 42
  43. 43. Uso di JUnit: asserzioni ✦ È possibile aggiungere anche una stringa come primo parametro per identificare la particolare condizione nell’output del test: • assertEquals(message, expected, actual) • assertTrue(message, cond) • assertFalse(message, cond) • ... 43
  44. 44. Uso di JUnit: esempio ✦ Supponiamo di voler realizzare una classe che effettui le addizioni tra numeri interi ✦ Seguendo i principi del TDD, cominciamo a definire l’interfaccia della classe: public class Adder { public int add(int a, int b) { // implementazione vuota! return 0; } } 44
  45. 45. Uso di JUnit: esempio ✦ Definiamo ora una classe di test per Adder nel file “AdderTest.java”: import org.junit.*; import static org.junit.Assert.*; public class AdderTest { // qui vanno i metodi di test } 45
  46. 46. Uso di JUnit: esempio ✦ Aggiungiamo un metodo per testare il metodo add di Adder: import org.junit.*; import static org.junit.Assert.*; public class AdderTest { @Test public void testAdd() { // qui va il codice del test } } 46
  47. 47. Uso di JUnit: esempio ✦ Infine scriviamo il codice del test, usando le asserzioni per specificare le condizioni che devono essere verificate: import org.junit.*; import static org.junit.Assert.*; public class AdderTest { @Test public void testAdd() { Adder a=new Adder(); assertEquals(7, a.add(4,3)); assertEquals(5, a.add(10,-5)); } } ✦ A questo punto la classe di test è completa e può essere compilata 47
  48. 48. Uso di JUnit: esecuzione ✦ Ci sono diversi modi di mandare in esecuzione i test: • dalla linea di comando • creando un main in Java • direttamente dall’ambiente di sviluppo Eclipse • ... 48
  49. 49. Uso di JUnit: esecuzione ✦ Dalla linea di comando: • java org.junit.runner.JUnitCore className... • esempio: foggia% java org.junit.runner.JUnitCore AdderTest JUnit version 4.5 .E Time: 0,008 There was 1 failure: 1) testAdd(AdderTest) java.lang.AssertionError: expected:<7> but was:<0> at org.junit.Assert.fail(Assert.java:91) at org.junit.Assert.failNotEquals(Assert.java:618) at org.junit.Assert.assertEquals(Assert.java:126) at org.junit.Assert.assertEquals(Assert.java:145) at AdderTest.testAdd(AdderTest.java:8) . . . . . FAILURES!!! Tests run: 1, Failures: 1 49
  50. 50. Uso di JUnit: esecuzione ✦ Dall’ambiente di sviluppo Eclipse: • dopo aver selezionato una classe di test, un package o l’intero progetto, si usa il comando: ‣ Run > Run As... > JUnit Test 50
  51. 51. Uso di JUnit: esecuzione ✦ Nota: nel riportare i risultati dei test, JUnit usa due termini diversi per indicare i problemi incontrati: • failure: il test è stato eseguito, e (almeno) una delle condizioni specificate è risultata falsa • error: si è verificato un errore che ha impedito l’esecuzione del test 51
  52. 52. Uso di JUnit: time-out ✦ È possibile specificare che un metodo di test debba considerarsi fallito se non completa il suo lavoro entro un tempo prestabilito • il time-out va specificato in millisecondi, come parametro dell’annotazione @Test @Test(timeout=3000) // 3 secondi public void testAdd() { // implementazione del test // per il metodo ‘add’ ....... } 52
  53. 53. Uso di JUnit: eccezioni ✦ È possibile specificare che un metodo di test debba sollevare una specifica eccezione, oppure si considera fallito: • l’eccezione va specificata passando l’oggetto Class che la rappresenta come parametro di @Test @Test(expected=NotFound.class) public void testSearch() { // implementazione del test // per il metodo ‘search’ ....... } 53
  54. 54. Uso di JUnit: Fixture ✦ Spesso occorre realizzare più test che utilizzano un insieme di oggetti comuni ✦ Dover creare questi oggetti all’interno di ciascun metodo di test porta a una duplicazione del codice ✦ Per ovviare a questo inconveniente, in JUnit si crea una “fixture”: un insieme di oggetti (associati a variabili di istanza della classe di test), che vengono reinizializzati prima di lanciare ogni test 54
  55. 55. Uso di JUnit: Fixture ✦ L’annotazione @Before consente di specificare un metodo che deve essere lanciato prima di eseguire ciascuno dei metodi di test ✦ In questo modo è possibile specificare come inizializzare la fixture prima dell’esecuzione di ciascun test ✦ È disponibile anche l’annotazione @After per specificare operazioni da eseguire dopo ciascun test 55
  56. 56. Uso di JUnit: Fixture import org.junit.*; ✦ Esempio: import static org.junit.Assert.*; public class AdderTest { private Adder a; @Before public void setUp() { a=new Adder(); } @Test public void testAddPositive() { assertEquals(7, a.add(4,3)); } @Test public void testAddNegative() { assertEquals(5, a.add(10,-5)); } } 56

×