Programmazione Java Avanzata Librerie fondamentali Ing. Giuseppe D'Aquì
Testi Consigliati Eclipse in Action (David Gallardo, Ed Burnette and Robert McGovern), Manning (2003) JUnit Cookbook [http://junit.sourceforge.net/doc/cookbook/cookbook.htm] Log4J Manual [http://logging.apache.org/log4j/1.2/manual.html] Ant manual [http://ant.apache.org/manual/index.html]
Unit Testing Unit Testing è un metodo con cui vengono verificate singole parti di un software Si testano, in modo automatico, parti molto piccole (singole classi o singoli metodi) Si controlla che la classe e i suoi metodi si comportino nel modo corretto I test vengono scritti ed eseguiti in fase di sviluppo, non sono presenti nel software finale
Esempio di Unit Test Esame e = new Esame(); [ ] e.setvoto(40); if(e.getvoto() >30) println( Errore, il voto non può essere maggiore di 30 );
Unit Testing (2) Serve a verificare che le singole parti funzionino perfettamente Ogni test deve essere indipendente dagli altri Non si testa l'interazione tra gli oggetti Ogni test rappresenta un contratto sul comportamento di un singolo oggetto o funzione
Unit Testing (3) I test sono scritti in modo da verificare il comportamento di un oggetto in condizioni estreme (es. input non valido) Spesso sono scritti prima del relativo codice che testano (Test Driven Development, TDD) E' come per le interface: definiamo qual è il risultato che vogliamo ottenere, postponendo i dettagli implementativi I test, infine, sono una sorta di documentazione: sono esempi di come un oggetto si comporta
Unit Testing: Pro Forzano il disaccoppiamento tra le singole unità: se ogni parte deve essere testata indipendentemente, siamo obbligati a progettarle in modo indipendente Prevengono un buon numero di errori ancora prima che il software venga eseguito Sorreggono le modifiche : cambiando il codice di un oggetto rischiamo di danneggiarne il funzionamento; con i test veniamo subito a conoscenza di questi errori Facilitano il debug
Unit Testing: Contro Se i test sono scritti male, potremmo avere dubbi sul funzionamento reale del software I test sono altro codice da scrivere Se un test improvvisamente fallisce, bisogna subito indagare per capire perché È impossibile testare tutte le ramificazioni e le condizioni possibili Se le singole unità funzionano, non è detto che l'insieme funzioni ancora (vedi Integration Testing)
JUnit È un framework per Unit Testing in Java È open source È ben integrato con gli IDE Ogni test prende il nome di test case Insiemi di test case compongono una test suite Gli oggetti preparati prima del test (riutilizzabili in più test) prendono il nome di fixture
JUnit Ogni test prende il nome di test case Insiemi di test case compongono una test suite Gli oggetti preparati prima del test (riutilizzabili in più test) prendono il nome di fixture Mock object sono oggetti finti, utilizzati per testare in modo indipendente le singole parti
JUnit: fixture Spesso il comportamento di un oggetto dipende da come è inizializzato, oppure dall'insieme di dati che gli vengono forniti Es. Classe Studente probabilmente viene inizializzata con una matricola, oppure legge i dati da un DBMS Quindi le fixture servono ad inizializzare oggetti, o riempire database con dati di prova Soto tutti dati che verranno scartati a test finito In JUnit: metodi setup() e teardown()
JUnit: Mock Object Se ogni oggetto deve essere testato indipendentemente dagli altri, come testare quelli che sono intimamente legati tra loro? Es. Se la classe Studente funziona solo quando riceve dati da un DBMS, come possiamo testarla senza testare anche il DBMS? La soluzione è creare un oggetto fittizio (Mock), che sembra un oggetto reale ma in realtà è vuoto e non fa computazioni Restituisce solo quello che ci interessa
JUnit: Mock Object (2) Esempio: Studente preleva i dati da un DBMS Il DBMS è un oggetto che fa le query e restituisce un ArrayList (metodo query() ) Creiamo una classe MockDBMS, che deriva da DMBS, riscrivendo il metodo query() in modo che restituisca sempre lo stesso ArrayList Librerie come EasyMock creano in automatico i Mock Object senza bisogno di scriverli a mano
Software Building Alla fine dello sviluppo, il codice non è tutto quello che va consegnato : Dati inclusi (DBMS, file di configurazione) Documentazione Programma di installazione o pacchetto Sono tutte operazioni: Necessarie Importanti Ripetitive Manualmente, si rischia di fare errori gravi
Software Building (2) Un processo di build è in genere composto da una sequenza di comandi, esempio: compila il file X producendo il file Y comprimi il file Y insieme ai due file W e Z invia quello che ottieni sul server remoto Il nostro lavoro può essere semplificato automatizzando il processo di build
Software Building (3) L'automatizzazione può avvenire all'interno del proprio IDE, ma ci sono alcuni problemi Altri sviluppatori potrebbero avere un IDE diverso Il processo potrebbe dover avvenire quando noi non siamo presenti Il processo potrebbe dover partire da un server remoto È difficile portare il processo su un altro IDE se dovessimo cambiarlo È difficile scambiarsi la definizione del processo
Software Building (4) Per questi motivi l'industria del software usa (da sempre) software specifici per il build Uno dei più famosi è Make, che ha implementazioni su vari sistemi (GNU Make, Microsoft Nmake) In genere si utilizza un linguaggio dichiarativo, il più possibile indipendente dal sistema
Ant Apache Ant è uno dei più famosi software per il build automation in Java Ha un linguaggio dichiarativo basato su XML È multipiattaforma Ha un insieme di task per compiere qualunque operazione
Ant (2) Il file di configurazione (build.xml) si basa su tre concetti: Progetto (project) Obiettivo (target) Operazione (task) Un task è una singola operazione (es. copia file, compila ) Un target è una sequenza di task pensata per raggiungere un certo obiettivo; un target può essere dipendente da un altro
Ant: esempio di build.xml
Maven È un sistema di build automation molto simile ad Ant È maggiormente orientato alla gestione completa di un progetto, mentre Ant si focalizza di più sulle operazioni Si basa sulla convention over configuration, per cui progetti diversi che usano Maven tenderanno ad avere una struttura simile con le stesse convenzioni
Continuous Integration È un insieme di tecniche, che combinano la Build Automation e lo Unit Testing, per avere report continui su: Lo stato di avanzamento dei lavori di sviluppo Lo stato di salute dell'integrazione
Logging In progetti non amatoriali è bene avere un sistema che tenga traccia dell'esecuzione di un software Trovare la parte malfunzionante può essere molto lungo, perché dobbiamo provare sezioni di codice una alla volta Procedere con un debug via breakpoint può essere: Lungo, se il progetto è grande Impossibile, se il software non gira sulla nostra macchina
Logging Spesso si usa, per avere un'idea di quello che sta succedendo, l'output su console: System.out.println() Non è una soluzione ideale: La console deve essere tenuta sotto controllo da una persona competente Per salvare l'output dobbiamo realizzare script al di fuori del nostro programma Tutto l'output viene inviato nello stesso luogo: non c'è modo di separare per priorità o per sottosistema
Logging Un Log è un file di testo che raccoglie l'output da un programma È usato normalmente per segnalazione e per debug È fondamentale quando: Il software è eseguito in modalità non-interattiva (es. server) Si vogliono raccogliere dati sull'uso e i comportamento di determinati oggetti Si vuole avere un resoconto delle funzioni eseguite
Apache Log4J È una libreria usata nei progetti Java È ormai uno standard de facto per il logging Definisce dei livelli di log, a seconda della gravità: FATAL ERROR WARN INFO DEBUG TRACE
Apache Log4J: uso La configurazione ci permette di scegliere la destinazione (appender) dei nostri log Una volta configurato si può usare in modo immediato: log.info( inizio copia file... ); log.warn( Il file non esiste, riprovo ); Si possono creare log che traccino solo un sottoinsieme del sistema: Logger log = Logger.getLogger( it.unirc.pja );