You are on page 1of 59

UNIVERSIT DEGLI STUDI DI TRIESTE

FACOLT DI INGEGNERIA
Corso di Laurea specialistica in Ingegneria Informatica

Tesi di Laurea in Prorgammazione Web

Progetto e realizzazione di uninfrastruttura di test per un sistema PACS

Laureando Giacomo Petronio

Relatore Prof. Eric Medvet Correlatore Ing. Andrea Poli Anno Accademico 2010-2011

Alla mia famiglia

Sommario
Introduzione ................................................................................................................................ 1 Motivazioni .................................................................................................................................. 2 Qualit del software..................................................................................................... 2 Software Testing .......................................................................................................... 2 Testare un PACS ........................................................................................................... 3 Marcatura CE ............................................................................................................... 3 Analisi .......................................................................................................................................... 6 Il sistema O3-DPACS..................................................................................................... 6 Il processo attuale ........................................................................................................ 8 Teoria dei test ............................................................................................................ 13 Incidenza dei bug nel sistema .................................................................................... 15 Analisi delle classi e package...................................................................................... 17 Database Testing ........................................................................................................ 19 Test funzionali ............................................................................................................ 20 Tecnologie utilizzate .................................................................................................................. 22 JUnit ........................................................................................................................... 22 Classi Stub e Mock (Mockito) ..................................................................................... 24 DBUnit ........................................................................................................................ 27 Dependency Injection ................................................................................................ 27 Sviluppo ..................................................................................................................................... 30 Creazione di unit test ................................................................................................. 30 Database Testing (jdbc, stored procedure, jpa)......................................................... 35 Creazione dei test di integrazione ed MBean ............................................................ 41 Test dei servizi ............................................................................................................ 44 Automazione ............................................................................................................................. 48 Script di build ed esecuzione automatici ................................................................... 48 Utilizzo di Jenkins ....................................................................................................... 49 Conclusioni ................................................................................................................................ 50

Risultati ottenuti ........................................................................................................ 50 Confronto ................................................................................................................... 50 Glossario .................................................................................................................................... 51 Bibliografia................................................................................................................................. 51 Appendice.................................................................................................................................. 52

Introduzione
Lo scopo di questa tesi quello di progettare e realizzare uninfrastruttura per migliorare il processo di creazione ed esecuzione dei test in O3-Enterprise, con particolare attenzione sul programma software O3-DPACS. Il raggiungimento di questo obbiettivo passa attraverso lanalisi del software oggetto dei test, lidentificazione dei principali problemi che si vengono ad affrontare in un sistema di tipo server, la ricerca di soluzioni e di pratiche comuni anche tramite lutilizzo di strumenti appositi, la creazione di test ed infine lautomazione della loro esecuzione. Leventuale miglioramento ottenuto viene quindi valutato mettendo a confronto il processo di test precedente con quello proposto.

Nota: mi scuso per le innumerevoli occorrenze della parola test 1

Motivazioni
Spesso, programmando sia per scopi personali che professionali, ci si ritrova a creare ed effettuare dei test senza porsi particolari domande sulla reale utilit o necessit di impiegare del tempo per questo tipo di operazioni. Pur sembrando ragionevolmente ovvio che i test debbano essere effettuati in quanto necessari per verificare il corretto funzionamento dellapplicazione, non altrettanto lampante quale sia la giusta quantit di test a cui sottoporre il software. Si ritiene quindi lecito interrogarsi su quante risorse sia opportuno impiegare nella fase di test, e se linvestimento avr un reale ritorno e di che tipo. Testare un programma un processo lungo che non a costo zero. I test vanno innanzitutto creati, eseguiti, e ne vanno verificati i risultati; il tutto va poi rieseguito pi volte nel corso della vita di un progetto. Per ottenere tutto questo necessario quindi allocare tutta una serie di risorse preziose e di diverso tipo: tempo, conoscenza (test vengono creati da persone che hanno conoscenza del software) ed esperienza (esistono figure professionali che fanno dei test la loro specialit). Come se non bastasse, tutti i test che vengono creati nel tempo, pongono lulteriore problema della manutenzione che riguarda sia il software oggetto di verifica che i test stessi. Un test che non viene aggiornato di pari passo con il software, diventa un test inutile con relativo spreco di risorse. Da questo generico quadro della situazione si pu intuire il fatto che il processo di test ha un costo non irrilevante. Per quale motivo quindi uno sviluppatore, ed a maggior ragione unazienda, dovrebbe decidere di investire sui test? Di seguito vengono esposte tre motivazioni, partendo da quella pi generica valida per qualsiasi software, arrivando a quella pi pratica, valida per un azienda come O3-Enterprise che crea dispositivi medici.

Qualit del software


Come si detto, lo scopo dei test riassumibile nel concetto di miglioramento della qualit del software. Questo concetto, pur essendo a prima vista astratto, ha un valore molto importante che pu assumere diversi aspetti; nel caso di unazienda, questi si riconducono tutti prima o poi ad un mero valore economico. Pi la qualit del software alta, pi alto il valore stesso del programma. Come si pu, allora, calcolarne il valore? Non possibile calcolare il livello assoluto della qualit del software tramite misurazioni dirette, per questo motivo si procede con un approccio differente. Ci si pu chiedere invece che cos che fa diminuire la qualit di un software; siccome il numero degli errori del software (o almeno quelli di cui si a conoscenza) un parametro che pu essere calcolato, ed essendo questi errori portatori di una diminuzione della qualit del software, allora si pu affermare che il software con il minor numero di errori un software generalmente migliore di uno con pi errori (a parit di funzionalit).

Software Testing
Il software testing un metodo empirico di investigazione per ottenere informazioni sulla qualit del prodotto. Tale metodo pu essere utilizzato per ottenere informazioni di vario tipo. Esso consente di avere una visione sulla presenza o meno di bug (errorei nel software); sulla effettiva 2

conformit a degli standard che il software dovrebbe essere in grado di gestire; sullo stato di avanzamento del progetto, evitando rilasci prematuri dovuti a componenti software non abbastanza robusti o addirittura mancanti; sui limiti del software stesso e sui requisiti hardware, tramite lutilizzo di stress-test.

Testare un PACS
Oltre alle motivazioni valide per qualsiasi tipo di programma software, per i programmi di tipo PACS, ed in generale per qualsiasi programma ospedaliero, sono valide delle ulteriori motivazioni per investire sui test. I programmi informatici ospedalieri seguono delle linee guida dettate da organizzazioni internazionali (principalmente IHE) composte sia da utenti che da produttori, che hanno lo scopo di armonizzare i diversi programmi che solitamente coesistono allinterno di uno o pi sistemi informativi ospedalieri, e lo fanno incentivando lutilizzo di standard condivisi e procedure comuni (DICOM, HL7, SOAP). Allinterno di un sistema informativo pi grande infatti, ve ne sono diversi che hanno la necessit di condividere dati anagrafici, clinici e diagnostici dei pazienti. Lo scambio di dati viene quindi facilitato indicando taluni protocolli di comunicazione e standard piuttosto che altri, in maniera tale da spianare la strada ai produttori di software che si trovano gi con delle specifiche, seppur generiche, da cui partire, e che consentono di poter avere un certo grado di confidenza sul fatto che il loro prodotto sar capace di interfacciarsi con prodotti terzi. O3-DPACS non si sottrae a tutto questo discorso, ed anzi trova nei protocolli e negli standard indicati, dei requisiti contro cui pu e deve essere testato.

Marcatura CE
Il programma O3-DPACS, come accennato, un software ospedaliero, classificato a tutti gli effetti come dispositivo medico in quanto utilizzato per effettuare diagnosi, e come tale per poter essere commercializzato in Europa, deve possedere dei requisiti minimi di qualit e di sicurezza, garantiti della marcatura CE. I dispositivi medici sono raggruppati in quattro classi che vanno da un livello minimo ad un livello di massimo rischio per il paziente fino alla morte, e sono le classi I, IIa, IIb, III. Lattribuzione ad una di esse dipende dalla destinazione duso indicata dal produttore, e pu variare a seconda delle peculiarit del dispositivo. La classificazione dei sistemi PACS argomento controverso in quanto non solo essi sono di difficile collocazione allinterno delle quattro tipologie, ma la classificazione stessa pu variare a seconda delle caratteristiche della particolare implementazione sotto esame. Per facilitare la loro classificazione, la comunit europea ha pubblicato delle linee guida per i dispositivi medici borderline, secondo le quali a seconda delle funzionalit offerte, un PACS pu ricadere soltanto nelle prime tre classi. Il motivo per il quale questa classificazione importante, che per i dispositivi a partire dalla seconda classe in poi, necessario lintervento di un ente terzo che autorizzi il produttore ad apporre la marcatura CE. Secondo queste linee guida, il sistema O3-DPACS rientrerebbe nella classe I, per la quale la marcatura potrebbe essere assegnata in autonomia. altres vero che nei bandi di gara europei non raro che tra i requisiti che le aziende debbano possedere, vi sia una certificazione del sistema qualit. La ditta O3-Enterprise produce anche dispositivi di classe II (workstation radiologiche), per i quali necessaria la verifica della qualit del processo di creazione dei software della ditta. La certificazione, ottenuta tramite ente terzo predisposto, valuta esclusivamente la qualit del processo e non del 3

prodotto finale. Per questo lazienda ha creato dei protocolli da seguire rigidamente in modo da rendere tale processo trasparente e valutabile. Poich queste linee guida si applicano a tutti i prodotti dellazienda, anche la creazione di O3-DPACS dovr seguire lo stesso procedimento di controllo. Ovviamente la certificazione pu essere concessa o meno in base al giudizio che viene dato al processo utilizzato per lo sviluppo del software. La definizione del processo, pur non essendo vincolata rispetto ad una normativa specifica, trova in diverse normative europee delle linee guida su cui consigliabile basarsi, e che se seguite possono garantire una maggiore probabilit che il processo venga certificato. La norma di riferimento per i dispositivi medici la ISO 13485. La ISO 13485 una norma che specifica i requisiti per i sistemi di gestione della qualit che permettono ad una organizzazione di dimostrare la sua capacit di fornire dispositivi medici che siano conformi ai requisiti regolamentari applicabili a tali dispositivi medici. basata sulla ben pi nota EN ISO 9001:2000 ma con delle modifiche riguardanti la parte di soddisfazione del cliente e al miglioramento continuo. Essendo una norma generale che si riferisce a dispositivi medici anche molto diversi tra loro, si deve individuare quale parte della norma sia applicabile al processo di test in questione . La parte relativa alla pianificazione della realizzazione del prodotto richiede esplicitamente che siano definite delle attivit di verifica, convalida, monitoraggio, ispezione e prove specifiche sul prodotto. Queste attivit devono produrre dei risultati che devono essere conservati e sono necessari a fornire evidenza che i prodotti soddisfino i requisiti. Nella sezione relativa al controllo dei dispositivi di monitoraggio e misurazione si richiede che lorganizzazione individui un sistema di monitoraggio e misurazione da effettuare al fine di fornire evidenza sulla conformit dei prodotti rispetto ai requisiti, ed assicurarne loggettivit dei risultati. Ed ancora nella parte dedicata alla fase di design e sviluppo del prodotto, viene richiesto che i risultati di questa fase, che hanno sempre lo stesso fine dellassicurare che il prodotto soddisfi i requisiti prefissati, siano documentati e conservati per dimostrare che il prodotto sia stato effettivamente verificato. Infine, nellattivit di misura dellefficacia della qualit del sistema, la norma prevede che queste attivit, fra le quali rientrano i test, devono essere descritte includendo il metodo utilizzato, il criterio di accettazione o rifiuto, lambiente in cui sono eseguiti, i dati di test utilizzati, i risultati ottenuti. La ISO 13485 non per lunica norma che tratta di dispositivi medici, ve ne sono molte altre, ma di sicuro interesse per quanto riguarda lo sviluppo di software per dispositivi medici vi la CEI EN 62304. Questa norma descrive il processo relativo al ciclo di vita del software ed al suo interno si fa esplicita menzione al sistema di test. In particolare la parte di testing del sistema software approfondisce le norme gi descritte nella 13485 per quanto riguarda i risultati derivanti dal design e dallo sviluppo e successiva verifica. La norma introduce il concetto di unit software nel processo di sviluppo del programma, e richiede che il produttore definisca dei metodi e delle procedure per la verifica di queste unit, e che queste verifiche siano effettuate attraverso lesecuzione di test. Ciascuna verifica, inoltre, deve essere validata secondo un criterio di accettazione definito a priori. Tra i criteri menzionati vi sono: Eventi eseguiti con una sequenza predefinita Allocazione di risorse in misura prevista Corretta gestione degli errori Inizializzazione delle variabili Condizioni al confine 4

importante notare che richiesto che alla termine di questa fase, i risultati dei test vengano conservati. Ciascuna unit software, al termine dello sviluppo e successiva verifica, deve essere integrata nel sistema nel suo complesso. In questa fase vanno definite altre procedure di verifica, questa volta atte ad assicurare che con lintegrazione dellunit software non siano stati introdotti difetti nel software integrato in precedenza (regression testing). Di tutti i test definiti ed eseguiti, il produttore deve essere in grado di fornire il risultato dei test (successo, fallimento e lista di eventuali anomalie), una documentazione sufficiente che consenta di ripetere il test, e lidentit di chi ha eseguito il test. Linsieme delle unit software integrate nel sistema vanno a comporre il programma completo, al quale il produttore deve prevedere un ulteriore set di test indispensabili per verificare che tutti i requisiti del software siano stati coperti. In conclusione, quello che si evince da queste normative che il miglioramento del processo di test (creazione, esecuzione, verifica) assume unimportanza chiave al fine della marcatura CE. Creare uninfrastruttura per O3-DPACS che faciliti il test di porzioni di software che presentano difficolt nella loro definizione dovuti alla particolarit dellambiente server su cui il programma viene eseguito, non si limita a migliorare la qualit intrinseca del prodotto, ma anche un tassello fondamentale per la certificazione del sistema qualit di O3-Enteprise.

Analisi
Il sistema O3-DPACS
Introduzione Il programma oggetto dei test di questa tesi, O3-DPACS, come gi anticipato, limplementazione di un sistema PACS Picture Archiving and Communication System, ovvero un gestore di immagini medicali, con finalit di archiviazione, trasmissione e visualizzazione delle immagini diagnostiche digitali. I file che vengono gestiti allinterno del sistema PACS sono in formato DICOM; esso uno standard che definisce sia il formato delle immagini digitali che il protocollo di comunicazione atto a inviare e ricevere immagini, oltre che ad effettuarne la loro ricerca. In un tipico caso duso, i file DICOM vengono creati dalle modalit diagnostiche (TAC, ecografie, etc.) ed inviati al PACS che le archivia e le rende disponibili per la consultazione. La consultazione, tipicamente remota, avviene tramite lutilizzo di workstation di refertazione, le quali cercano e recuperano le immagini dal PACS; il tutto avviene utilizzando il protocollo di comunicazione e formato dei file DICOM. Le funzionalit tradizionali di un PACS sono quindi: Acquisizione dei file DICOM prodotti dalle modalit diagnostiche Archiviazione, per la gestione delle informazioni sulle immagini acquisite Indirizzamento dei dati verso le stazioni che lo richiedono (informazioni e immagini)

A queste funzionalit va aggiunta anche il servizio di Worklist, ovvero la gestione degli appuntamenti, sempre tramite DICOM. Oltre alle funzionalit base, O3-DPACS offre una serie di servizi secondari indispensabili, dedicati allintegrazione con i sistemi informativi ospedalieri in cui viene installato. Queste integrazioni sono rese possibili grazie alladozione di un protocollo molto diffuso negli ambienti ospedalieri, HL7. Questo protocollo lo standard de-facto per lo scambio dei dati clinici fra sistemi informativi diversi. I principali servizi offerti da O3-DPACS ed implementati tramite HL7 sono: Aggiornamento anagrafica pazienti Riconciliazione dei pazienti1 Inserimento ordini Gestione visite

Unulteriore integrazione offerta da O3-DPACS data dalla possibilit di pubblicare in un sistema centrale di condivisione di documenti, un particolare tipo di file DICOM (Key Object Selection) tramite web-services. Questo file contenente riferimenti ad altre immagini DICOM, rende noto al sistema di integrazione di livello superiore, che nel PACS vi sono le immagini relative allesame del paziente di turno, e che sono disponibili per il loro recupero (IHE XDS-I). Per gestire le diverse configurazioni e per navigare tra i contenuti del PACS, vi anche un apposita applicazione web che comunica con i servizi del PACS e ne pu pilotare il comportamento. Questa

In un sistema informativo pu capitare che vi siano due entit paziente che si riferiscono alla stessa persona fisica. Il procedimento che riconduce le due entit in una sola viene chiamato riconciliazione

interfaccia web in grado di effettuare operazioni di base quali la ricerca tra contenuti, visualizzare immagini, gestire utenti e ruoli, e alcune operazioni di manutenzione. Fino a qui sono state descritte le funzionalit macroscopiche di O3-DPACS da un punto di vista dei casi duso. Da questo elenco per sono stati tralasciati i dettagli implementativi in quanto sono stati oggetto di analisi approfondite e che vengono esposti brevemente di seguito per dare un idea generale della variet di tecnologie coinvolte nel progetto; questi dettagli verranno poi descritti con maggiore accuratezza nelle rispettive parti in cui esporr lapproccio utilizzato per implementare i casi di test. Tecnologie coinvolte O3-DPACS unapplicazione server, scritta in Java, che viene fatta eseguire allinterno di un Application Container per Java Enterprise Edition, in particolare JBoss (4.2 e 5.1). La parte di comunicazione tramite protocollo DICOM implementata estendendo le funzionalit offerte dalle librerie open-source dcm4che, le quali offrono anche gli strumenti per la manipolazione dei file in formato DICOM. Queste librerie nascondono parte del protocollo a pi basso livello (gestione dei socket sia client che server, e il parsing dello stream di dati). Laltro protocollo di comunicazione esplicitamente creato per lo scambio di dati clinici HL7. Per la gestione di questo canale di comunicazione il PACS sfrutta la libreria hapi, che similmente a quanto avviene con le dcm4che, fornisce un livello di astrazione utile per evitare la gestione dei socket e la manipolazione dello stream dati. I servizi web esposti per lintegrazione con XDS utilizzano invece una libreria scritta appositamente dal team di O3, condivisa con altri progetti; la gestione delle connessioni per i web service trova ausilio nelle librerie httpClient di Apache, manipolando quindi direttamente i messaggi SOAP. Lapplicazione web per fornire linterfaccia utente utilizza la tecnologia JSF (Java Server Faces), al cui vertice vengono eseguiti componenti di tipo RichFaces. La visualizzazione delle immagini stata recentemente rivista e si basa invece sul framework Adobe Flex. Tutte le informazioni che vengono gestite da O3-DPACS vengono immagazzinate in un DBMS. Il PACS in grado di funzionare sia utilizzando DBMS Oracle che MySql. La possibilit di utilizzare uno o laltro, come verr spiegato in seguito, comporta un notevole sforzo in termini di programmazione e conseguentemente anche di creazione dei test. Le comunicazioni tra applicazione e DBMS avvengono con metodi diversi: query composte anche a runtime ed eseguite tramite JDBC; stored procedure richiamate sempre tramite JDBC; utilizzo di Hibernate come framework ORM (Object Relational Mapping). La gestione dei singoli servizi del PACS, tra cui la loro configurazione e ciclo di vita, stata implementata con JMX; i diversi MBean che rappresentano i servizi, espongono metodi per la loro gestione. Le implementazioni vere e proprie dei servizi fanno invece largo uso di Enterprise Java Bean.

Questa breve ma intensa carrellata di tecnologie utilizzate da O3-DPACS (non esaustiva) ha lo scopo di evidenziare la quantit e la variet di strumenti adottati nel sistema. Questo fatto ha avuto un impatto non trascurabile, in quanto una buona parte del tempo stata impiegata per il lavoro di analisi delle diverse componenti, requisito fondamentale per poi costruire, laddove possibile, delle pratiche, o addirittura dei piccoli framework, per creare i test veri e propri.

Il processo attuale
Viene ora descritto quello che il tipico processo di test utilizzato in O3-Enterprise per il programma O3-DPACS, ma che viene applicato ad ogni progetto allinterno della ditta. Il processo documentato in dettaglio nel manuale di qualit, e viene seguito fedelmente in quanto anche da questo dipende la certificazione ISO 13485. Ogni intervento (o caso, in inglese issue) su di un programma deve passare attraverso una serie di stati, a partire dallapprovazione da parte del technical manager, che ne valuta le motivazioni. Il caso, se ritenuto necessario, viene assegnato ad uno sviluppatore, il quale come primo compito ha quello di effettuare lanalisi del problema e redigere un documento. Dallanalisi si passa alla stesura del design dopo approvazione del documento da parte del capo progetto, che dovr approvare anche il documento di design. Solo a questo punto lo sviluppatore potr iniziare a scrivere il codice vero e proprio su di un branch del progetto (il branch in un sistema di versionamento del codice un ramo della linea principale, chiamata trunk). Al termine della scrittura del codice, si passa alla fase di test. I test vengono inseriti durante ciascuna di queste fasi, ma vi il vincolo che per ogni requisito individuato nella fase di analisi, vi sia almeno un test corrispondente. Se la fase di test si conclude positivamente, il caso viene marcato come pronto per essere integrato nel sistema. Durante lintegrazione, che viene sempre svolta dal project manager, il codice prodotto dallo sviluppatore per il caso in oggetto e presente nel branch del sistema di versionamento, viene portato nella versione corrente del progetto (nel trunk), e vengono rieseguiti tutti i test definiti per quellissue.

Col passare del tempo il progetto viene modificato e gli issue integrati si accumulano. Quando viene deciso che il programma pronto per essere rilasciato (secondo politiche aziendali), gli viene assegnata una versione, e vengono rifatti tutti i test definiti in ciascun issue integrato a partire dallultimo rilascio. Per procedere con il rilascio, vi sono degli ulteriori test obbligatori che vanno svolti per verificare le funzionalit fondamentali del programma (una sorta di regression test); infine viene scelto un campione di test da issue datati (di rilasci precedenti) in maniera casuale.

interessante notare che ciascun test inserito, viene eseguito un minimo di tre volte durante il ciclo di vita dellapplicazione. Ovviamente durante la fase di sviluppo, i test molto probabilmente vengono effettuati anche molte volte, a seconda del giudizio dello sviluppatore. Dallanalisi del sistema di bug tracking utilizzato in O3, sono state estratte delle informazioni relative allandamento del numero di test in relazione a ciascuna versione e tempi di rilascio.

Versione 1.5 1.6 1.7 1.7.1 1.7.2 1.8 1.8.1 1.9

release date 06/09 09/10 09/10 10/10 10/10 05/11 06/11 11/11

# issue 12 40 9 1 1 24 3 5

# test 0 50 21 1 1 57 3 26

media test/issue 1.25 2.3 2.35 1 5.2

Tabella 1 - Analisi storica dei test

Nota: le versioni a tre cifre non fanno parte del ciclo programmato dei rilasci, ma sono state create per risolvere in velocit dei bug importanti; il numero di issue e test relativi sono bassi per questo motivo. Inoltre la prima versione rilasciata (la 1.5) stata fatta quando ancora non cera un processo di qualit definito, da cui la presenza dello zero nel numero dei test creati! Sebbene i numeri siano bassi, si possono comunque notare delle tendenze: Il numero di test per issue in aumento La frequenza dei rilasci aumenta

Dalla somma di questi due fattori se ne ricava che in futuro sar richiesto di eseguire sempre pi spesso un numero maggiore di test. Da qui un ulteriore conferma dellesigenza di un sistema pi efficiente che possa essere daiuto per rendere questo processo pi rapido. Pro e contro del metodo attuale Dallanalisi del processo di testing in uso si cercato di cogliere quali sono i punti di forza, valorizzandoli quanto possibile, e quali sono invece le debolezze, proponendo soluzioni che possano essere integrate nel sistema attuale. Regression test Una prima osservazione possibile farla sui regression test. I test di questo tipo hanno lo scopo di verificare che una modifica sul codice non abbia compromesso funzionalit non direttamente collegate alla parte sottoposta a miglioramento. Nel metodo attuale i regression test individuati sono quelli eseguiti ad ogni rilascio, e comprendono sia quelli definiti come obbligatori, sia quelli scelti a caso tra i test degli issue pi vecchi. Questo approccio ha sicuramente la sua validit, ma il punto debole che vengono eseguiti solamente ad ogni rilascio, evento con frequenza bassa (circa 2 rilasci annui programmati). Leventuale aumento della frequenza dei rilasci permetterebbe s di effettuare regression test pi spesso, ma richiederebbero troppo tempo in quanto alcuni di questi test sono anche di difficile esecuzione, e mancano completamente di automatismi. Allapproccio corrente va imputato un secondo punto a sfavore. I test designati come obbligatori ad ogni rilascio non riescono a coprire in maniera adeguata tutte le funzionalit. Riuscendo a creare dei test automatici, la parte di codice verificata con i regression test potrebbe aumentare di molto senza incidere troppo sul tempo impiegato per la loro esecuzione. Eventuali test automatici, inoltre, potrebbero essere eseguiti molto pi spesso, senza dover aspettare il rilascio, ma potrebbero ad esempio essere eseguiti anche ad ogni integrazione di un issue. Unit test I test che vengono definiti per ciascun issue non sono quasi mai di tipo unit test. Questa particolare tipologia di test viene utilizzata per verificare le funzionalit di singole unit logiche di codice, spesso 10

trovando corrispondenza nella definizione di un singolo metodo di una classe. Lo scopo quello di accertarsi che una classe esponga dei metodi che facciano esattamente quello che ci si aspetta dalla loro definizione. Per come stato pensato il processo esistente, questo tipo di test viene completamente ignorato. Dai colloqui avuti con agli sviluppatori di O3, emerso che pratica comune creare unit test durante la fase di programmazione, per esempio creando metodi Main allinterno delle classi in cui viene verificato il comportamento della classe stessa. Purtroppo per questo codice non viene versionato e i test vengono persi. La definizione di una pratica comune che indichi come creare questo tipo di test, magari utilizzando un framework apposito come JUnit, comporterebbe, a fronte di uno sforzo minimo, il salvataggio di questi test che possono essere facilmente resi automatici e rieseguibili a piacimento. Scripted testing La maggior parte dei test che vengono creati in O3-Enterprise rientrano invece nella categoria test script (un'altra etichetta che viene utilizzata per definire questo tipo di test checklist test). Un test cos definito composto da una serie di istruzioni scritte che devono essere eseguite sul sistema sotto test per verificarne il funzionamento. Quello che contraddistingue questo tipo di test che le istruzioni sono scritte in maniera informale, e lesecuzione non automatizzata ma destinata ad una persona che deve eseguire le istruzioni secondo quanto scritto. Questi test non scendono quasi mai al di sotto di un certo livello di dettaglio, e quello che testano sono principalmente dei casi duso del programma. Per creare un test di questo tipo va specificato: Ambiente di esecuzione (compreso lo stato iniziale del sistema) Una serie di istruzioni da eseguire in sequenza Un risultato aspettato

Analizzando questo tipo di approccio si identificano due fasi principali: progettazione del test ed esecuzione del test. Ciascuna di queste fasi presentano le seguenti criticit. Esecuzione L'esecutore del test deve comprendere l'intento di chi ha scritto il test. L'intenzione del creatore del test non sempre esplicita nella descrizione e va quindi compresa dal contesto. Se non c' la comprensione dell'intento reale del test, allora l'esecuzione del test diventa una mera esecuzione di istruzioni (il che non sempre male, ma la potenzialit di un test creato ed eseguito da una persona pu essere sfruttata molto meglio, per i test automatici si possono sfruttare le altre tecniche oggetto della tesi) Design Colui che deve scrivere il test deve essere in grado di indovinare con una certa precisione il livello di conoscenza del tester che eseguir il test. La scrittura di test richiede pazienza, bravura nello scrivere, un livello minimo di empatia con l'esecutore del test (che talvolta sconosciuto). Questo un processo tipicamente noioso, lo sviluppatore non invogliato a scrivere test ed una immediata conseguenza la cattiva qualit dei test stessi. Nel caso di applicazioni complesse, per fare in modo che un tester sia in grado di eseguire un test, a meno che non sia lui stesso uno degli sviluppatori dell'applicazione, necessario fornire una descrizione molto dettagliata dei passi da eseguire, a livello anche molto basso. Es. 11

Mettere un breakpoint nella classe X alla riga Y e poi alla riga Z Lanciare l'applicazione in debug Al punto Y verificare che il valore della variabile A sia diversa da null Proseguire facendo loperazione P Al punto Z verificare che il valore della variabile A sia pari al valore contenuto nella colonna C della tabella T con chiave primaria K. Questo tipo di descrizione di test pu sembrare estremo, ma ci sono casi in cui necessario e viene effettivamente utilizzato. Un test cos fatto presenta diversi problemi. Un primo problema che nel momento in cui il codice verr modificato, la descrizione del test diverr senza senso (le righe di codice in cui mettere i breakpoint non corrisponderanno pi). Se la modifica riguarda porzioni di codice ampio con relativo refactoring del codice (variabili rinominate, o metodi spostati da una classe all'altra), allora l'intero test avr perso di significato. Un ulteriore problema dato dal fatto che con test di questo tipo il tester non acquisisce nessuna conoscenza dell'applicazione, n aumenta la sua abilit nel testare. Non ci sono per solo fattori negativi in questo tipo di test, che invece uno strumento di verifica con potenzialit molto elevata. La capacit di una persona che legge il test e lo esegue, permette lesecuzione di test anche molto complessi in tempi relativamente brevi. Questo tanto pi vero quante pi operazioni di tipo diverso tra loro sono richieste dal test. Si pensi anche ad esempio a ai test necessari per verificare che lapplicazione web abbia laspetto voluto. Probabilmente prima o poi si potrebbe arrivare alla definizione di un test automatizzato che riesca a fare la stessa cosa, ma con tempi estremamente alti, e con una fragilit altrettanto alta. invece molto pi rapido e probabilmente anche molto pi affidabile relegare questo tipo di verifiche ad una persona che in grado di giudicare se i requisiti sono soddisfatti o meno. Oltre alle interfacce, test di questo tipo sono indispensabili laddove le componenti coinvolte sono numerose e di tipo anche molto diverso tra loro. Generalmente si tratta di test di integrazione a pi alto livello in cui la funzionalit testata coinvolge programmi esterni con cui il sistema deve comunicare. Test di integrazione si trasformano in test dei casi duso, dove prima o poi si dovranno testare le funzioni dellapplicazione dallinizio alla fine, senza ricorrere a strumenti che simulano componenti esterne, cosa che come spiegher, un requisito quasi fondamentale per lautomazione dei test. Per sfruttare meglio questo sistema quindi necessario fare una riflessione sullobbiettivo di ogni singolo test. Va quindi evitato di scrivere dei script-test con un livello di dettaglio troppo elevato o troppo specifici (unit test), in quanto troveranno applicazione nei sistemi di automazione proposti nella tesi; va invece preferito questo tipo di test laddove lautomazione di difficile realizzazione. Ripetibilit dei test Un ultima considerazione sul processo esistente va fatta sulla ripetibilit dei test, intesa sia come difficolt nel ripetere un test, sia come probabilit che un test venga rieseguito pi volte. Gli attuali test sono definiti tutti nella stessa maniera (script-test), sia che essi vengano eseguiti con una certa frequenza (come ad esempio i regression test), sia che la loro utilit si esaurisca allinterno dellissue in cui vengono definiti. La mia proposta mira a catalogare i test in queste due macro tipologie. Il lavoro di creazione di test e loro automazione si concentrer quindi sui test ad elevata ripetibilit. 12

1. 2. 3. 4. 5.

Allinterno poi di questa grande categoria, andr a distinguere ulteriormente i test in base ad altri criteri che vengono esposti di seguito.

Teoria dei test


Definizione Il software testing viene definito come il processo di operare un sistema o una sua componente sotto particolari condizioni, osservando o registrando i risultati, e valutando alcuni aspetti del sistema o della componente (IEEE standard 610.12-1990). una tecnica che tipicamente da indicazioni su come effettuare le seguenti operazioni: Analizzare la situazione di partenza Modellizzare lo spazio dei test Decidere quali parti del sistema coprire Determinare le discriminanti per il successo/fallimento (oracoli) Configurare il sistema di test Eseguire il sistema Osservare il sistema Valutarne i risultati

Da questa lista sintetica si ricava la metodologia descritta di seguito per creare un test. Un programma pu essere immaginato come una collezione di variabili in ingresso e in uscita, ed necessario analizzare quali sono i valori che possono assumere. A partire da questi valori, si procede applicando un modello di campionamento che separi tutti i possibili valori in diversi gruppi a seconda della loro capacit a far fallire il programma in esame. Per ogni variabile, dividerne i possibili valori in gruppi di equivalenza. Creare un test prendendo almeno un valore da ciascun gruppo cos determinato. Sar poi necessario decidere quali combinazioni di variabili sono da considerare. Si devono decidere le condizioni finali del sistema in base alle quali possibile concludere che un test riuscito o fallito. Descrivere la configurazione del sistema e dellambiente in cui esso viene fatto eseguire. Solo a questo punto possibile eseguire il test, osservarne il comportamento e valutare il successo dal risultato. Tipi di test In letteratura si usa catalogare i test in base al loro scopo e destinazione duso. Di seguito vengono illustrati i 5 tipi di test pi utilizzati. Esistono ovviamente anche altre categorie, che vengono ricavate secondo i criteri pi diversi, ma ritengo che quelle che seguono sono di gran lunga quelle pi riconosciute dai maggiori testi che trattano dellargomento e dalle risorse in rete.

13

In questa suddivisione, il criterio una miscela tra loggetto sottoposto a verifica e lo scopo della verifica. Man mano che ci si muove dal livello pi basso a quello pi alto, i test diventano pi funzionali e richiedono che porzioni sempre maggiori dellapplicazione siano presenti fino ad arrivare alle due categorie pi estese dove si verifica il programma nella sua totalit. Unit test Lo scopo principale degli unit-test verificare che lapplicazione si comporti come da requisiti e scovare difetti nel codice nelle primissime fasi di sviluppo. Anche se i test di altro tipo come i test funzionali perseguono gli stessi obbiettivi, gli unit-test offrono altri vantaggi per i quali vengono scelti: Maggiore copertura del codice rispetto a test funzionali Incoraggiano il refactoring Individuano regressioni Documentano il comportamento voluto Introducono delle metriche di misura del codice

Gli unit test permettono una copertura del codice non raggiungibile con altri sistemi. Questa una possibilit che deve essere sfruttata laddove ve ne sia una reale necessit. La copertura del 100% del codice un obbiettivo che comporta un notevole impiego di risorse, con un ritorno in termini di efficacia discutibile. Vi sono per dei casi in cui una copertura il pi ampia possibile di porzioni di codice particolarmente sensibili da ricercarsi. Unit test hanno anche il vantaggio di riuscire a simulare condizioni di errore che sono altres molto difficili da ottenere con test funzionali. Essendo tra i primi utilizzatori del codice scritto, gli unit-test mettono per primi in evidenza eventuali difetti nellarchitettura del codice. Scrivere test costringe lo sviluppatore a creare codice facilmente testabile, isolato (basso accoppiamento), e con delle API (interfacce di programmazione) chiare ed esplicative. Una serie di test creati per una classe anche un efficace metodo per documentare il comportamento voluto della classe e un valido esempio di utilizzo di quella classe. Anche per gli unit test si possono individuare delle categorie di test, distinguibili secondo un criterio che verr utilizzato durante la creazione dei test per O3-DPACS. Queste categorie si differenziano per i confini del codice sotto test.

14

Tipo di test Test di unit logiche Test di integrazione di unit

Descrizione Test che si focalizza su un singolo metodo. I confini possono essere controllati utilizzando metodi quali la creazione di Mock e Stub Test che si concentra sullinterazione tra diverse unit logiche. Il numero di unit coinvolte pu variare fino a coinvolgere anche componenti di tipo diverso (DBMS, file system, network) Test che estende i suoi confini fino a coinvolgere stimoli esterni allapplicazione e verificare la reazione del programma. La definizione di questo tipo di test si basa sui casi duso

Test funzionale di unit

Tabella 2 - Tipi di unit-test

Linterazione fra i diversi tipi di test sono illustrati meglio nella seguente illustrazione. Lutilizzo di tutti e tre i tipi consente di coprire bene lapplicazione e conferire maggiore confidenza quando si tratta di fare dei cambiamenti.

Esistono altri due tipi di test molto utilizzati, ma che non rientrano nello scopo della tesi e che includo per completezza. Questi sono stress-test e test di accettazione. I primi vengono utilizzati per conoscere quali sono i limiti strutturali dellapplicazione, come ad esempio il numero di utenti contemporanei che il programma in grado di servire con prestazioni accettabili, su di un determinato hardware. Il secondo tipo di test viene invece utilizzato quando vi un committente che paga per il prodotto che si sta sviluppando ed lo strumento decisionale per capire se sono stati soddisfatti i requisiti del cliente. Questi test sono spesso dei test di casi duso e di performance, ma non escludono anche test pi soggettivi come la valutazione dellaspetto dellinterfaccia. importante notare che i test di accettazione non vengono eseguiti dal team di sviluppo ma dal cliente stesso.

Incidenza dei bug nel sistema


Prima di proseguire con la progettazione di quello che sar il sistema di test da realizzare, si cercato di capire se esistono delle parti di programma pi esposte ad errori di altre su cui concentrare gli sforzi, ed stato fatto andando a vedere lo storico dei bug degli ultimi rilasci. O3-Enterprise utilizza un sistema di tracciabilit dei difetti (infelice traduzione personale di bugtracking system) per registrare tutti gli eventi legati al software, inclusi quindi i bug scovati e loro stato di risoluzione.

15

Dal lavoro di ricerca emersa la tabella presente in appendice, dalla quale sono stati estratti i seguenti dati riguardanti landamento generale dei difetti segnalati. Nota sulla classificazione dei bug: Minor: sono errori che non pregiudicano il funzionamento del sistema la cui risoluzione pu essere rimandata Major: difetti del software che vanno risolti quanto prima ma che non pregiudicano il funzionamento globale Critici: compromettono il funzionamento di almeno una parte fondamentale del programma ed hanno la priorit massima
PACSCORE
Minor Major Critical

PACSWEB
Minor Major Critical Minor

WADO
Major Critical Minor

totale
Major Critical

2011 2010 2009 2008

6 14 6 0

2 7 5 4

0 1 2 0

12 4 5 0

0 1 4 5

0 4 2 0

4 3 2 0

0 1 2 0

0 0 1 0

20 21 13 0

4 9 11 9

0 5 5 0

Tabella 3 - Analisi storica dei bug

Osservazioni:

Tra il 2009 e 2010 il numero dei bug critici e major rimasto praticamente invariato. Sono aumentati i bug minor. Tra il 2010 e 2011 non emerso nessun bug critico, e i major sono stati dimezzati. Il numero dei bug minor rimasto alto. Nel 2008 il sistema di bug tracking era ancora ai primordi quindi non pu far testo.

Il dato pi rilevante che il numero dei bug minor sempre alto. Difetti di questo tipo sono da imputarsi allintroduzione di nuove funzionalit del sistema. I controlli effettuati sui casi d'uso pi critici hanno raggiunto un buon livello di efficacia, rimane invece basso il livello sui servizi di nuova creazione, dove unit ed integration test possono fare la differenza. Dopo aver identificato quali tipi di test mancano maggiormente nel sistema, il passo successivo stato vedere se ci sono delle aree pi predisposte a difetti su cui concentrare il mio lavoro. Nella seguente lista sono incluse quelle parti di O3-DPACS che hanno avuto un incidenza maggiore di bug. Dal totale dei difetti del software registrati, sono qui riportate soltanto quelle aree alle quali possono essere attribuite almeno il 10% dei bug totali (per lelenco completo vedere la tabella in appendice): 1. 2. 3. 4. 5. Servizi dicom: 23% Gestione del DBMS: 14,5% Servizio di Worklist: 12,5% Gestione dei servizi del PACS (ciclo di vita e configurazioni): 10,4% Il restante 40% riguarda problemi non catalogabili in macro aree.

Questa lista vuole essere una sorta di scaletta da cui partire per affrontare il lavoro assegnando delle priorit. Va notato che analizzando lelenco completo con maggiore attenzione, si pu osservare come i problemi legati alla gestione del DBMS si trovano anche in aree diverse da quella

16

esplicitamente assegnatagli, acquisendo cos unimportanza ancora maggiore, motivo per il quale stato dato molto spazio ai test legati al DBMS.

Analisi delle classi e package


stata analizzata in dettaglio la struttura del sistema O3-DPACS con lo scopo di assegnare a ciascuna classe (o insieme di classi), il tipo di test che possibile creare. Per ogni classe stata valutata la possibilit di creare un unit-test corrispondente e leventuale utilit. In alcuni casi stato possibile, in altri invece ci estremamente difficile a causa di un alto livello di accoppiamento tra classi. Lalternativa quindi un test di integrazione che coinvolga la classe in esame assieme a tutta una serie di classi da cui esso dipende. In questultimo caso sono state indicate quali componenti sono coinvolte. Ad esempio per le classi che non mantengono uno stato interno ma offrono funzioni di utilit come potrebbe essere il calcolo di un hash, stata prevista la creazione di un unit-test. Per classi pi complesse come ad esempio quelle che gestiscono la persistenza dei metadati a partire da oggetti dicom, il test non pu che essere di tipo integrazione. Il criterio utilizzato per marcare le classi che possono essere delle buone candidate da sottoporre a unit test, stato vedere se di volta in volta le seguenti condizioni erano verificate o meno. Un test non unit-test quando: Comunica con un DBMS Comunica attraverso la rete Coinvolge il filesystem Ha bisogno di configurazioni speciali dell'ambiente

Un unit-test invece deve essere: Veloce Ripetibile Indipendente Auto esplicativo Opportuno

Inoltre qualsiasi classe che non abbia alcuna logica interna (classi 'entit'), non deve essere oggetto di unit test. Sono stati scartati metodi che accedono al database o alla rete, metodi che chiamano in cascata altre classi (a meno che non siano facilmente interpretabili da classi Stub o Mock), e sono inclusi metodi che hanno una logica il cui risultato cambia in base ai parametri d'ingresso. Metodi che modificano lo stato o il comportamento dell'applicazione non rientrano in questi casi.

17

package it.units.htl.dpacs.helpers Anonimizer Compression DateHelper DateTimeRange FileHasher GlobalConfigurationLoader MailerSystem PhysicalMediaTimerTask Unit test Unit test Unit test Unit test Unit test Integration test (DBMS) Integration test (DBMS) Integration test (DBMS) package it.units.htl.dpacs.valueObjects Tutte le classi (Privi di logica) package it.units.htl.dpacs.core VarLoader Unit test Package it.units.htl.dpacs.dao StoragePerformer UserManager Tutte le altre classi UidGenerator DcmFramesToMF DcmQuerier KosBuilder StudiesVerifierWorker XdsClient XdsMessageCreator Integration test (FileSystem) Integration test (DBMS) Functional test (Classi EJB) Unit Test Integration test (DB, FS) Integration test (Dicom, networking) Unit test Integration test (DBMS, FileSystem, networking) Integration test (networking) Integration test (FileSystem, DBMS) Package it.units.htl.dpacs.servers Tutte le classi Functional Test (Classi MBean) Package it.units.htl.dpacs.servers Tutte le classi Functional Test (Classi MBean) Package it.units.htl.maps Classi entit JPA Classi DAO (privy di logica) Unit test

Tabella 4 - Analisi delle classi e dei package

Questa tabella non esaustiva, il numero totale delle classi del progetto O3-DPACS circa 300. Le classi non in elenco sono state omesse in quanto interfaccie, classi abstract, classi appartenenti al modulo web, ed altre classi che hanno pochissima logica interna e sono per lo pi degli aggregatori di dati. Una nota particolare riguarda il modulo web. Esso presenta delle caratteristiche molto differenti da tutto il resto del progetto O3-DPACS. Al suo interno ci sono diverse servlet con della logica interna, filtri (es. per l'autenticazione), ed altre classi che sono strettamente legate con il ciclo di vita di 18

un'applicazione web. Il test di questo progetto richiede un approccio specifico, con l'utilizzo di tecniche e strumenti appositi. Ci sono inoltre delle integrazioni con tecnologie diverse da java (Flex), e la parte di presentazione si basa sulle Java Server Faces arricchite con le librerie di RichFaces, tutte infrastrutture che rendono particolarmente difficile la creazione di test automatici che richiederebbero strumenti specifici come JSFUnit, o Selenium. Vista quindi la natura di questo progetto e dopo l'analisi iniziale, stato scelto di tralasciare questa componente e concentrare gli sforzi sul test del resto del progetto.

Database Testing
Testare lo strato software che si occupa della gestione del DBMS pone diverse problematiche: Impossibilit di isolare la classe in esame (deve accedere al DBMS) Complessit del test (inserimento dati di test, pulizia finale) Velocit (overhead dovuto allapertura/chiusura delle connessioni e accesso al filesystem)

Per risolvere i primi due problemi si pu ricorrere a strumenti pi o meno complessi che vengono incontro alle esigenze dello sviluppatore che vuole evitare di dover scrivere troppo codice solo per creare unit test. In questi casi infatti la tentazione di evitare di scrivere test diventa sicuramente molto alta, e la probabilit di non aggiornare test vecchi lo ancora di pi. Uno di questi tool utilizzati DBUnit, un'estensione di JUnit che verr introdotta in seguito. Il terzo problema, quello legato alla velocit dei test, un problema che si pu definire tale nel momento in cui vi sono centinaia di test da eseguire prima di ogni build, rendendo vano di fatto il vantaggio di avere unit test eseguibili ad ogni modifica del codice. Nel caso dell'applicazione O3-DPACS questo problema ancora non si pone essendo stato sviluppato un numero di test del database non elevato. Il problema viene quindi rimandato, anche perch la soluzione pi gettonata dalla letteratura di questo momento quella di diminuire il tempo di comunicazione con il database portandolo direttamente sul computer dello sviluppatore (vi sono casi in cui pi sviluppatori si trovano costretti ad utilizzare un'unica istanza remota del database per fare test e sviluppare regolarmente). Ma possibile fare di meglio utilizzando un DBMS in memoria il quale non deve accedere alle risorse del filesystem diminuendo il tempo di accesso sia in scrittura che in lettura ai dati (Hypersonic DB, Derby, Java DB, SQLite). Purtroppo per non sempre possibile ricorrere ad un DBMS di questo tipo. L'applicazione O3DPACS infatti, sfrutta pesantemente le ottimizzazioni che sono ottenibili con le Stored Procedure di Oracle e MySQL. Non avendo uno strato di astrazione del database, l'applicazione non in grado di utilizzare database che non siano quelli sopracitati, n tantomeno database "volatili". Ritornando ai problemi legati alla complessit del codice necessario all'interazione con il database, i tipici test che vengono creati sono quelli che verificano la correttezza delle operazioni di inserimento ed estrazione dei dati. Test pi avanzati hanno a che fare con le chiamate a Stored Procedure. Il risultato di tutti questi casi, per, dipende fortemente dallo stato in cui si trova il database nel momento della loro esecuzione. Se al termine di un test di inserimento di dati in una tabella avente dei vincoli di unicit, il test non "pulisse" il database, una seconda esecuzione dello stesso test comporterebbe il fallimento dello stesso! Pensando invece al caso dell'estrazione di dati, il database dovrebbe essere opportunamente riempito con dei dati validi, ed eventualmente vuotato al termine del test. Tutte queste operazioni da 19

effettuare prima e dopo ogni test non sono impossibili da effettuare, ma sono sicuramente molto verbose da codificare, con unelevata probabilit di commettere degli errori, ritrovarsi poi a testare il codice del test stesso. DBUnit risolve parte di questi problemi permettendo di fatto una semplificazione della scrittura dei test di database, rendendo pi facile l'inizializzazione dello stato del database e riportandolo allo stato iniziale una volta terminato tutto. Nell'applicazione di O3-DPACS, la gestione dei dati svolge un ruolo di fondamentale importanza; per lo pi si le interazioni con il DB sono operazioni "delicate" in quanto essendo complesse, hanno maggior probabilit di contenere degli errori. Per questo motivo per la creazione dei test del database stata posta un'attenzione maggiore, cercando diverse strategie per semplificare il compito. I sistemi di accesso al database utilizzati nell'applicazione sono di vario tipo a seconda delle necessit. Per operazioni molto semplici come potrebbe essere la SELECT per ottenere il valore di una singola colonna in una tabella di tipo chiave-valore, vengono quasi sempre utilizzate le api JDBC in cui la query SQL residente nel codice stesso. Man mano che le operazioni da fare sono sempre pi complesse, l'approccio utilizzato diventa quello delle chiamate a Stored Procedure, che sono invece presenti nel DBMS. C' un ulteriore approccio, utilizzato prevalentemente in quella parte dell'applicazione dedicata all'interfaccia web, che sfrutta invece Hibernate come astrazione del database.

Test funzionali
Dopo aver individuato un possibile elenco di unit test e di integrazione, si passati allanalisi dei test che sono attualmente definiti, dai quali stata ricavata una lista di test che possono essere oggetto di automazione con conseguente riduzione dei tempi per la loro esecuzione. Questi test, essendo derivati da quelli attuali, sono tutti di tipo funzionale, e ciascuno di essi descrive un tipico caso duso dellapplicazione. Da notare che pochissimi dei seguenti test sono inseriti nella lista di quelli obbligatori da effettuare ad ogni rilascio. Una loro automazione consentirebbe di poterli inserire tutti senza problemi. Test funzionali sul protocollo DICOM o Test di storage (salvataggio di immagini e metadati) di pi studi (esami) contenenti dati su paziente e studio da individuarsi tramite classi di equivalenza. Il risultato dovrebbe essere un test eseguito pi volte con dati sempre diversi. o Test di retrieve (recupero di immagini e metadati) con dati individuati come sopra tramite classi di equivalenza. o Test di query (ricerca nellarchivio tramite metadati) Test funzionali sul protocollo HL7 o Gestione dei messaggi di update o Gestione dei messaggi di riconciliazione o Gestione dei messaggi su appuntamenti Test dei sistemi opzionali e di integrazione o Creazione ed invio dei messaggi XDS-I (web-service) o Servizio di Worklist (liste di lavoro) o Servizio di cancellazione studi o Anonimizzazione o Compressione studi o Generazione a runtime di file jnlp (java web-start) 20

o o o

Recupero di immagini via wado Task monitoraggio memorie di massa Test algoritmi (hash, base64)

21

Tecnologie utilizzate
Nellarco di tutto il periodo di lavoro su questa tesi, sono state valutate diverse tecnologie che potessero rivelarsi utili per facilitare la scrittura dei test. Quando in seguito varr descritto in dettaglio il processo che ha portato alla creazione dei test in un modo piuttosto che in un altro, ci saranno dei riferimenti a tre software in particolare pi alcune tecniche di programmazione che sono state impiegate. Queste software sono JUnit, Mockito, DBUnit mentre le tecniche di programmazione vedono lutilizzo del pattern Dependency Injection, lutilizzo di classi Stub e di classi Mock.

JUnit
Questo software probabilmente il framework per la creazione di test pi conosciuto e diffuso, e non soltanto in ambiente Java. Come suggerisce il nome, nato per facilitare la creazione di unit test. Non il solo framework esistente di questo tipo: il diretto concorrente TestNG (Next Generation). Questultimo un progetto pi recente, creato per colmare le principali lacune di JUnit: Supporto nativo ai test parametrici Ordine di esecuzione dei test Riesecuzione dei soli test falliti

Nonostante queste differenze la scelta ricaduta comunque su JUnit in quanto le stesse caratteristiche sono ottenibili tramite lutilizzo di plugin. Questi sono infatti molto pi numerosi proprio grazie al fatto che JUnit sul mercato da pi tempo, cosa che ha influito positivamente anche sul livello di adozione che pi capillare. A questo dobbiamo aggiungere il fatto che in Eclipse (lIDE di sviluppo utilizzato in O3-Enterprise) vi il supporto nativo a questo framework. Dalle interviste effettuate agli sviluppatori dellazienda era emerso che degli unit test vengono tipicamente creati durante la fase di sviluppo senza seguire una metodologia particolare, ed infine cestinati una volta accertato il corretto funzionamento della classe. Lusanza quella di creare un metodo Main allinterno della classe oggetto di verifica e provare a richiamarne i metodi. La verifica pu avvenire semplicemente osservando lo standard output sul quale vengono stampati i risultati dei metodi. Questo approccio presenta diverse debolezze: 1. Un unico test per classe. Potendo inserire un singolo metodo Main per ciascuna classe, anche il test pu essere definito una sola volta. Per ovviare a questo si commentavano man mano porzioni del metodo Main a seconda di quello che era il metodo da esaminare. 2. Esecuzione manuale di un test per volta. Per lesecuzione si deve specificare di volta in volta qual la classe sotto esame contenente il metodo Main. 3. Struttura variabile. Ogni sviluppatore utilizza un suo sistema, che pu variare anche da classe a classe, rendendo difficile il suo riutilizzo per chi non lautore. 4. Mix di codice operativo e codice di test nella stessa classe. Questo oltre ad essere stilisticamente brutto, rende anche difficile capire al volo se una classe ha un rispettivo test. 22

Tutti questi problemi si risolvono impiegando JUnit nel seguente modo: 1. La pratica comune suggerisce di creare una classe di test JUnit associata alla classe sotto esame. Al suo interno possibile creare tanti metodi quanti sono i casi che si vogliono verificare (tipicamente almeno uno per ogni metodo pubblico). 2. possibile definire una suite che raggruppa pi test JUnit. In questo modo possibile eseguire tutti casi definiti in tutte le classi della suite in una sola volta. 3. JUnit impone che ogni test-case sia annotato con @Test, rendendo esplicita la sua definizione. Inoltre prevede che ci possano essere dei metodi che vengono richiamati prima e dopo lesecuzione di ogni test, tramite lutilizzo di altre annotazioni. Tutto questo, assieme alle convenzioni di scrittura suggeriti da JUnit, permette di mantenere una struttura coerente anche tra sviluppatori diversi. 4. JUnit suggerisce che per ogni classe da testare, venga creata una classe con lo stesso nome a cui aggiungere il suffisso Test. In questo modo vi una chiara distinzione tra qual il codice operativo e quale ne verifica il funzionamento, mantenendo al contempo evidente la loro relazione. Unaltra convenzione utilizzata in questo lavoro quella di dichiarare nella classe di test, una variabile di nome instance dello stesso tipo della classe sotto esame. In questo modo sar evidente, in qualsiasi test, qual listanza della classe sotto test. Per chiarire meglio lutilizzo di questo strumento viene fornito un semplice esempio. Si supponga di avere una classe Anonimizer che espone un metodo pubblico anonimize che accetta come parametro un oggetto di tipo DicomMetaInfo. Si proceder quindi a creare una classe JUnit cos formata:

In JUnit i metodi annotati con @Before e @After vengono chiamati prima e dopo lesecuzione di ogni test, anche per quelli definiti nella stessa classe. Infatti per ogni metodo annotato con @Test, il motore di JUnit istanzia una classe distinta. In questo modo lesecuzione di un test non pu avere in 23

nessun modo conseguenze sullesecuzione di un secondo test. Se da un lato un vantaggio, dallaltro rende difficile lesecuzione di test dove lesecuzione di uno dipende dal risultato di un altro. Poich i metodi contrassegnati come Before ed After vengono richiamati in ogni test-case definito, non il posto ideale dove effettuare delle operazioni di inzializzazione costose in termini di tempo (come ad esempio la connessione al DBMS). Queste operazioni, dove possibile, consigliabile inserirle in metodi annotati con @BeforeClass ed @AfterClass. Questi metodi, che devono essere statici, vengono richiamati solamente una volta per tutti i metodi @Test definiti nella classe. In questo caso una limitazione deriva dal fatto che tutte le risorse inzializzate in questi metodi devono per forza di cose essere statiche, e condivise tra tutti i test contenuti nella classe. Unultima nota riguarda la collocazione fisica delle classi di test. La presenza simultanea di classi vere e rispettive classi di verifica allinterno dello stesso package pu risultare caotica. Per ovviare al problema possibile sfruttare le possibilit offerte dagli IDE di sviluppo. In Eclipse ad esempio si possono definire pi cartelle per i sorgenti di uno stesso progetto. Mantenere una cartella separata contenenti i sorgenti dei test rende il progetto pi strutturato. Questo faciliter anche il compito dellautomazione dellesecuzione dei test; come verr mostrato, sar possibile programmare un task che esegua tutti i test-case di JUnit presenti soltanto nella cartella specificata.

Classi Stub e Mock (Mockito)


I principi su cui si fonda il concetto di unit-test che sono stati esposti finora, per i quali un test deve verificare il comportamento di una sola classe o addirittura di un solo metodo, sono difficilmente riscontrabili nella realt. Il motivo che le classi che non hanno dipendenze verso altre classi (che non siano quelle della distribuzione standard di Java), in un progetto vero, sono poche. tipico il caso in cui ci sono dei riferimenti a classi di utilit per fare delle conversioni, per recuperare configurazioni varie, o per avere lo stato di altre classi da cui si dipende. Tutto questo per, oltre ad essere indice di una tecnica di programmazione datata in quanto non pensata per agevolare la creazione di test, pone dei problemi che devono essere comunque risolti. Lobbiettivo finale isolare completamente la classe di cui vogliamo verificare il comportamento dal resto del sistema. Per riuscire in questo compito si pu procedere nel seguente modo: identificare quali sono le variabili e i loro tipi che creano dipendenze con il resto del programma. Una volta identificate, necessario capire come sostituirle in qualche modo con delle variabili di tipo simile (stessa interfaccia, o classi figlie). Queste nuove classi devono essere create di volta in volta, e la loro 24

logica interna va programmata in base alle esigenze di quel specifico test; spesso sufficiente creare delle classi completamente prive di logica. Questa una tecnica che viene utilizzata per creare delle classi fantoccio, le quali possono assumere due nomi differenti a seconda di alcuni dettagli sulla loro particolare implementazione. Si parla quindi di classi Stub2 e di classi Mock3. Sebbene entrambe queste tecniche abbiano lo scopo di sostituire delle classi che non devono far parte del test, le classi Stub si differenziano in quanto vengono create in maniera tale che forniscano una risposta predefinita per il test in atto. Solitamente non sono in grado di rispondere diversamente a seconda dei parametri di chiamata. Un buon esempio di classe Stub potrebbe essere un finto servizio web che per un determinato test deve rispondere 404. Una classe Mock, invece, qualcosa di pi raffinato, in quanto pu essere programmata per rispondere in maniera diversa a seconda del tipo di chiamata e dei parametri. Un Mock creato per un test specifico viene configurato in modo che si comporti in un certo modo, e si aspetta di ricevere delle chiamate secondo una determinata specifica. Un'altra differenza data dal tipo di verifica che si vuole effettuare quando si utilizza uno o laltro tipo. Nel caso degli Stub la verifica viene solitamente fatta sullo stato finale della classe: dopo la chiamata ad un servizio che risponde 404 la classe si porr in uno stato particolare che si andr a controllare. Nel caso dei Mock invece, la verifica pi orientata al comportamento. Rimanendo sullo stesso esempio, si potrebbe controllare che il finto servizio web, impersonificato questa volta da un Mock, venga effettivamente chiamato dalla classe in oggetto e che non riceva altre richieste. Lo stato finale della classe in oggetto meno importante. Da un punto di vista del loro utilizzo, importante notare che estremamente pi semplice creare delle classi Stub. Esse devono infatti comportarsi solamente in un modo, non hanno stato interno e i loro metodi forniscono sempre lo stesso risultato. Le classi Mock sono tuttaltro che semplici da creare: esse devono mantenere uno stato interno, collezionare informazioni sul numero e lordine delle chiamate ai vari metodi, informazioni da cui dipendono i valori restituiti dai metodi esposti. Per questo motivo sono emersi dei framework che facilitano la loro creazione ed utilizzo. Nel mondo Java in questo momento esistono almeno 6 progetti di questo tipo: EasyMock, jMock, Mockito, Unitils Mock, PowerMock e JMockit. Lutilizzo di un tale strumento sicuramente fondamentale per questo lavoro, ma il confronto delle loro caratteristiche dichiarate ha reso evidente che le differenze non sono tali da giustificare una comparazione pi accurata.

2 3

Stub, termine inglese che significa matrice, cio lelemento originale da cui si creano le copie Mock, termine inglese che significa fantoccio, finto

25

Caratteristica
Verifica numero invocazioni di un metodo Mock parziale Zero configurazione (inizializzazione contesto) @RunWith non necessario Confronto semplificato tra oggetti VO Mock in cascata Supporto Mock interfacce multiple Auto injection dei Mock Mock di Enum Single jar file

Easy Mock

jMock

Mockito

Unitils Mock

Power Mock

JMockit

Si Si Si Si Si No No No No No

Si No Si Si No No No No No No

Si Si Si Si Si Si Si Si No Si

No Si Si No Si Si No Si No No

Si Si No No Si No No No No No

Si Si Si Si Si Si Si Si Si si

Tabella 5 - Confronto tra Mocking framework

Appurato il fatto che tutti gli strumenti sopracitati fossero in grado di creare dei Mock a partire dalla classe da impersonificare, il criterio utilizzato per la scelta stato quello della curva di apprendimento. Sono stati analizzati diversi esempi del loro utilizzo, in base ai quali stato scelto mockito che gi dalle prime prove, si rivelato molto intuitivo. Questo fatto pu sembrare di poca importanza, ma considerando che dovr venire introdotto a diversi sviluppatori, questo stato un motivo sufficiente per far ricadere la scelta su questo prodotto. Un ulteriore punto a favore per mockito deriva dalla quantit e peso delle librerie da introdurre nel flusso di lavoro. Lunica dipendenza necessaria infatti una singola libreria da meno di 1,5Mb. Per iniziare ad utilizzare mockito sufficiente aggiungere al classpath lunico jar richiesto. Dopodich possibile creare Mock a partire da qualsiasi classe. Il ciclo di vita dei Mock in mockito il seguente: Creare un Mock Object sulla classe o interfaccia Dichiarare il comportamento atteso Utilizzo indiretto (da parte delloggetto sotto verifica) Verifica dellutilizzo (come stato usato)

Di seguito un esempio sul suo utilizzo (preso e opportunamente semplificato da uno dei casi di test creati per O3-DPACS). In questo caso si voleva creare un Mock dellinterfaccia java.sql.DataSource. Quando veniva richiamato il metodo getConnection, la connessione restituita doveva essere quella configurata per quel particolare test, che veniva opportunamente inizializzata in un metodo annotato con @Before (di JUnit).

26

Loggetto sotto esame objectUnderTest, al quale viene passato il finto DataSource. Questo viene programmato per restituire la connessione opportunamente inizializzata quando gli verr invocato il metodo getConnection. Alla fine del test ci si accerta che il metodo sia stato effettivamente invocato.

DBUnit
DbUnit un'estensione di JUnit, ed ha come unico scopo quello di facilitare il movimento di dati dentro e fuori dal DBMS, utilizzando dei "dataset" come astrazione dei dati. I dataset di DbUnit possono assumere diverse forme, ma la pi comune quella di un file xml contenente i dati veri e propri. DbUnit viene quindi utilizzato per inizializzare il database portandolo in uno stato conosciuto, contenente esattamente quei dati che servono al test. In questo modo possibile mantenere questi dati in un file xml assieme alla classe di test, con conseguente versionamento. DbUnit si rivela molto utile anche per confrontare un set di dati estratti dal database tramite il test con un dataset preciso. Una particolarit interessante di DbUnit che l'inserimento di un dataset nel database pu essere fatto ordinando a DbUnit di rimuovere dati preesistenti che corrispondo al dataset in questione in modo tale da avere un set di dati sempre "pulito" su cui lavorare. Molto comoda anche la funzione che permette di creare un dataset a partire da dati presenti in un dato DBMS, filtrando i dati per tabella o per valori.

Dependency Injection
La Dependency Injection (DI) un pattern di programmazione nato con lo scopo di migliorare la testabilit di sistemi software, oltre che facilitare la creazione di componenti indipendenti. Talvolta il concetto che ne sta alla base viene riferito anche con il nome di Inversion Of Control (IoC). Si pu

27

trovare anche sotto il nome di Hollywood Principle (dont call us, well call you), che ho personalmente reinterpretato come il principio del colloquio di lavoro (le faremo sapere). Un componente un aggregato di codice scritto e pensato per essere utilizzato, senza modifiche, da unapplicazione che fuori dal controllo di chi lo ha creato. Senza modifiche significa che il programma utilizzatore non ne pu cambiare il codice sorgente, anche se gli permesso di modificarne il comportamento estendendolo nella misura prevista dal creatore del componente. Per comprenderne il principio pu essere utile accompagnare la spiegazione con un esempio esplicativo. In questo esempio il modulo definito dalla classe GlobalConfiguration espone un metodo che restituisce tutte le propriet di una certa categoria.
class GlobalConfiguration... public Property[] propertiesFor(String category) { List allProperties = finder.findAll(); for (Iterator it = allProperties.iterator(); it.hasNext();) { Property prop = (Property) it.next(); if (!prop.getCategory().equals(category)) it.remove(); } return allProperties.toArray(new Property[allProperties.size()]); }

Sorvolando sulla qualit del codice, quello che importante notare che la classe utilizza un oggetto finder per recuperare tutte le propriet. Per far s che questo modulo sia indipendente dal sistema utilizzato per la gestione delle propriet, si pu estrarre un interfaccia per il finder che dichiari un metodo findAll. In questo modo la classe GlobalConfiguration pu ignorare lattuale implementazione del finder.
public interface PropertyFinder { List findAll(); }

A questo punto per, da qualche parte si deve pur inizializzare la variabile finder con la sua implementazione, ad esempio nel costruttore.
private PropertyFinder finder; public GlobalConfiguration() { finder = new TextFileGlobalConfiguration("configuration.txt"); }

Questa implementazione del PropertyFinder legge le propriet da un file di testo. Ma cosa succede se questo modulo viene utilizzato da unapplicazione che per vuole utilizzare un database al posto del file di testo? Siccome GlobalConfiguration utilizza linterfaccia PropertyFinder, sufficiente crearne una seconda implementazione che legga i dati dal DBMS. Rimane per il problema che il finder viene inizializzato nel costruttore; stata infatti creata una dipendenza verso TextFileGlobalConfiguration.

28

Dal diagramma delle classi si vede che GlobalConfiguration dipendente sia dallinterfaccia che dalla sua implementazione. Per ovviare a questo problema e fare in modo che lunica dipendenza sia quella verso linterfaccia, si utilizza la tecnica della Dependency Injection. Lidea di base avere un oggetto separato che si assume la responsabilit di assegnare la corretta implementazione dellinterfaccia in GlobalConfiguration.

Il risultato quello che nei manuali di design pattern viene chiamato plugin pattern. Ora dovrebbe anche essere chiaro anche il motivo del nome Hollywood Principle (dont call us, well call you). La classe GlobalConfiguration passiva rispetto alle sue dipendenze, si aspetta che qualcun altro si prenda la responsabilit di inizializzare un implementazione adeguata del PropertyFinder e che gli venga notificato. Questo il principio di base, poi ci sono diverse interpretazioni che differiscono per il modo in cui le dipendenze vengono soddisfatte. Tipico il caso delle dipendenze passate come parametri nel costruttore. Altre volte si prevede un implementazione di default, modificabile tramite setter (questo il metodo pi utilizzato nel lavoro di refactoring eseguito su parte di O3-DPACS per renderlo testabile).

29

Sviluppo
Per la creazione dei casi di test per O3-DPACS stato seguito un determinato ordine. Le prime classi che sono state controllate sono quelle che dallanalisi sono risultate idonee per essere sottoposte a test di unit logiche. Va sottolineato il fatto che la creazione di questo tipo di test fatta a posteriori ha poca valenza se il fine scovare eventuali errori. La maggior parte del codice che stato sottoposto a questo controllo esiste da diversi anni e viene regolarmente usato senza riscontrare problemi. Il momento della creazione degli unit-test, invece, dovrebbe essere contestuale alla creazione delle singole classi. Lo scopo coprire con dei test quella parte di codice di pi basso livello, cercando di mettere in difficolt il sistema con i dati pi critici. Il motivo per il quale sono stati comunque creati questi casi di test di unit, per identificare le difficolt comuni e proporre delle soluzioni. Inoltre, con la creazione di buon numero di casi, sar possibile verificare che il processo di automazione della loro esecuzioni funzioni correttamente. Lo sviluppo proseguito con la verifica di tutto ci che comunica con un DBMS, cercando le soluzioni migliori per i diversi casi (query dirette via JDBC, chiamate alle Stored Procedure, accesso ai dati tramite JPA). Nella fase successiva sono state prese in considerazione le classi che interagiscono con pi unit, creando degli appositi test di integrazione. Alla fine si arrivati ai test funzionali passando per i servizi Dicom ed Hl7 del PACS (MBean ed EJB).

Creazione di unit test


Per questa tipologia, un buon punto di partenza stato il package it.units.htl.dpacs.helpers. Al suo interno vi si trovano una serie di classi di utilit che offrono funzioni che ben si prestano ad essere verificate in isolamento, analizzando loutput a seconda degli input forniti. Anonimizer La prima classe analizzata e testata la it.units.htl.dpacs.helpers.Anonimizer. Questa classe espone dei metodi che accettano un dataset Dicom, un oggetto che contiene dei metadati riguardanti un file Dicom (dati anagrafici del paziente, dettagli degli esami, etc.). Il risultato lo stesso dataset, ma spogliato di alcuni dati per impedire il riconoscimento del paziente. La creazione del test con JUnit in questo caso banale. Quello su cui stato possibile riflettere linsieme dei dati da utilizzare. Le variabili in ingresso sono numerose, e sono date dai campi del dataset che devono essere anonimizzati. Per ogni variabile per, si individuano solamente due classi di equivalenza: variabile valorizzata e variabile non valorizzata.

30

Variabili Dati paziente Nome Cognome Id Dati medico curante Nome Cognome

Classi equivalenza null null null null null valorizzato valorizzato valorizzato valorizzato valorizzato

null null null

valorizzato valorizzato valorizzato

In base a queste considerazioni, per questa classe stato sufficiente creare due casi per ogni metodo.

Si potrebbe pensare che due classi di equivalenza siano poche, ma questa considerazione nasce dallanalisi diretta del codice della classe Anonimizer. possibile vedere come i valori delle variabili non vengano mai lette, ma solamente sostituite con dei valori standard, previo controllo sul loro essere null o meno. Quando un test viene creato esplorando il codice sorgente delloggetto sottoposto a verifica, si dice che si sta eseguendo del white box testing. Questo si differenzia dal metodo di black box testing che non prevede che chi scrive i test abbia conoscenza dellimplementazione delloggetto. Il white-box testing ha il vantaggio che permette la copertura di una maggiore, se non totale, porzione di codice. Lo svantaggio sta nel fatto che il tester di solito lo stesso che ha sviluppato la classe in esame, e per questo meno obbiettivo. DateHelper La classe DateHelper fornisce un metodo getFirstUsefulDate che a partire da una stringa, restituisce data e ora corrispondente. Se la combinazione di data e ora rappresenta un instante nel passato, allora restituisce la prima occorrenza di quellora nel futuro. Questo metodo viene utilizzato per la configurazione di pi servizi, in particolare per far iniziare lesecuzione di diversi task temporizzati. In questo caso le classi di equivalenza sono pi interessanti, e vengono riportate nella seguente tabella con il rispettivo risultato aspettato.

31

Input D,T (data e ora) Null Formato di D,T non valido D,T > now() D < today(), T > now() D,T < now()

Expected Output (Date) Null Null Data e ora corrispondente a D,T Data del giorno corrente, ora pari a T Data del giorno seguente, ora pari a T

Il risultato di questa tabella la classe DateHelperTest con 5 metodi, uno per caso di test. DateTimeRange Con la DateTimeRange si rimane sugli strumenti per la manipolazione di date. interessante notare come per questa classe fosse gi previsto un test nel sistema preesistente. Questo uno dei pochi casi in cui stato esplicitamente definito una unit-test utilizzando lunico sistema che era previsto, quello degli script-test (istruzioni testuali, esecuzione manuale). DateTimeRange nasce dalla necessit di dover gestire intervalli di date in maniera precisa, prevedendo molte opzioni. Viene utilizzata per supportare linterrogazione delle liste di lavoro dato un certo intervallo di tempo. La complicazione arriva proprio dal numero di parametri e dai loro valori possibili. Questa classe pi complessa delle precedenti, ma completamente isolata dal resto dellapplicazione, per questo motivo viene riportata come esempio significativo di test di unit realizzato sul sistema. Il funzionamento prevede che ad unistanza vengano configurati gli estremi degli intervalli definiti da due estremi sinistri (data inizio, ora inizio) e due destri (data fine, ora fine). possibile impostare da uno a tutti e quattro i parametri per ottenere risultati differenti. Dopo aver inizializzato gli estremi voluti, possibile ottenere la data e ora (in formato java.util.Date) del limite destro e del limite sinistro dellintervallo impostato. Lanalisi dei possibili valori ha portato alla seguente tabella, che copre tutti i casi possibili.
DATE FROM X X X X X X X X TO Y Y Y Y Y Y Y Y TIME FROM J J J J J J J J Expected output TO K K K K / K K K K FROM x.j x.j X X x.j x.j X X J J <= <= <= <= <= <= <= <= <= <= <= <= <= <= <= <= DATA DATA DATA DATA DATA DATA DATA DATA DATA DATA DATA DATA DATA DATA DATA DATA <= <= <= <= <= <= <= <= <= <= <= <= <= <= <= <= TO y.k Y y.k Y y.k Y y.k Y K K -

32

La met sinistra della tabella copre tutti gli input possibili, mentre sulla sinistra viene riportato il risultato aspettato per ogni caso. La X e la Y rappresentano delle date, mentre la J e la K rappresentano delle ore. Le celle degli input contenenti il simbolo significano che lestremo corrispondente non impostato. Nelle celle delloutput invece significa che il limite indefinito. Da questa tabella stato creata una classe di test contenente tutti i 16 casi significativi individuati. FileHasher Ci sono casi in cui non possibile creare lo unit-test di una classe senza modificarne il codice. Questo un problema che si presenta frequentemente in O3-DPACS, come in tutti i programmi scritti senza avere in mente questo obbiettivo. La classe FileHasher ne un buon esempio. Questa semplice classe computa lhash di un file passato come parametro assieme al nome dellalgoritmo da utilizzare. Al suo interno per va ad interrogare un gestore di propriet del sistema per conoscere la dimensione del buffer da utilizzare. Questa dipendenza non consente di testare la funzionalit di hash poich, preso al di fuori del contesto in cui si aspetta di essere eseguita, il gestore di propriet non funziona (in questo caso un MBean che interroga un DBMS). Il refactoring di questa come di altre classi, ha come vincolo fondamentale quello di mantenere laddove possibile, linterfaccia della classe modificata invariata. Questo requisito necessario per assicurarsi che gli utilizzatori sparsi nel resto dellapplicazione, non debbano a loro volta essere intaccati, rimanendo sicuri che tutto continuer a funzionare. Analizzando la classe sotto esame si vede come il gestore di propriet viene richiamato allinterno del metodo doHash. Per non modificare questo comportamento (giusto o sbagliato che sia), stata aggiunta una propriet a livello di classe contenente il valore della dimensione del buffer, ed un relativo metodo setter.

Nella funzione doHash stato aggiunto un controllo in modo che il gestore venga interrogato solamente se il setter non stato esplicitamente chiamato. A questo punto stato possibile creare il test, che dato un file di riferimento, verifica che lhash restituito dal metodo sia corretto tenendo conto dellalgoritmo utilizzato. Gli algoritmi di hash testati sono SHA-1, SHA-256 e MD5. Per la Figura 1 - diagramma di flusso del metodo doHash realizzazione stata sfruttata la funzionalit di modificato JUnit per rieseguire lo stesso test-case pi volte variandone i parametri. Il parametro variato lalgoritmo, mantenendo fisso il file di riferimento; ovviamente anche il risultato aspettato varia con lalgoritmo, quindi i parametri passati al test sono la coppia algoritmo-risultato atteso.

33

UidGenerator In Dicom, ogni immagine univocamente identificata da un UID (Universal ID), formato da una serie di caratteri numerici separati dal carettere punto. Il valore finale la composizione di pi parti: Root id: identificativo univoco dellorganizzazione che crea luid delle istanze Study Instance UID: id relativo dello studio (esame) Series Instance UID: id relativo della serie (ogni studio pu avere n serie) SOP Instance UID: id relativo della singola immagine.

Esempio di Sop Instance UID:

La classe UidGenerator viene utilizzata per generare uid con la UidGenerator root id assegnata ad O3-Enterprise. La cosa interessante che data unistanza, possibile generare degli UID per nuovi studi, + getNewStudyUid() : String serie ed istanze. Qui non ci sono input in base ai quali cambia il + getNewSeriesUid() : String + getNewInstanceUid() : String risultato, ma lo stato interno della classe che cambia in base ai + reset () metodi invocati (senza parametri) ciascuno dei quali restituisce luid del tipo richiesto. Di conseguenza lobbiettivo del test non sar quello di verificare il risultato in base ai parametri di ingresso, ma sar quello di verificare loutput ad ogni cambiamento di stato. Lo stato di ogni istanza dato dalla terna UID dello studio (X), UID della serie (Y) e UID dellistanza (Z). possibile modellizzare la classe come una macchina a stati da cui possibile ricavare la seguente tabella delle transizioni:
T1 Stato inizale Evento Effetto X;Y;Z getNewStudy X++ T2 X;Y;Z getNewSerie Y++ T3 X;Y;Z getNewInstance Z++ T4 X;Y;Z reset X = newStudyUid(); Y = newSeriesUid(); Z = 1; Stato finale X+1;Y;Z X;Y+1;Z X;Y;Z+1 X;Y;Z

Nella teoria dei test si parla di 0-switch coverage quando i test creati sono pari al numero di transizioni. In questo caso i casi sarebbero 4. Fermo restando che la copertura totale di una classe di questo tipo impossibile (esistono infiniti stati), per aumentare la copertura con i test, necessario provare un numero maggiore di combinazioni stato-transizione. Lo 1-switch coverage prevede che vengano esplorate le combinazioni scendendo di un livello nellalbero delle combinazioni, con conseguente incremento esponenziale dei casi di test.

34

Figura 2 - 1-switch coverage per UidGenerator

Lalbero in figura rappresenta tutte le diramazioni possibili fino al secondo livello. I cerchi rossi indicano gli stati e transazioni appartenenti allo 0-switch, mentre aggiungendo i cerchi rossi si raggiunge lo 1-switch coverage, con un totale di 16 casi di test. Ciascun test parte dallo stato rappresentato dalla radice e arriva fino alla foglia pi esterna, coprendo tutte le transizioni previste dal percorso. KosBuilder Fra tutti gli unit-test creati, lultimo caso che viene proposto stato scelto perch si presta bene allintroduzione di un problema comune in molte parti del codice di O3-DPACS. La classe KosBuilder ha il compito di costruire un file dicom particolare detto KOS (Key Object Selection), che al suo interno non contiene immagini ma solamente riferimenti a istanze di altri file dicom. Il kos viene creato in base ai parametri di ingresso, che in questo caso un oggetto KosMetaInfo, contenente tutti i dati necessari per ottenere un file kos. Il problema nel testare questa come altre classi, dato dal fatto che il test dovrebbe verificare che il file generato contenga i dati corretti secondo quanto passato in ingresso. Questa operazione pu essere vista come una sorta di conversione dal tipo A al tipo B. La verifica dovrebbe dire se listanza di A equivale allistanza di B. Per fare questo quindi utile creare dei comparatori che siano in grado di confrontare istanze di tipo diverso tra loro. Per il caso di KosBuilder stata creata una classe KosComparator costruita in maniera tale da verificare che i dati provenienti dal KosMetaInfo siano stati correttamente riportati nel relativo file KOS. A questo punto per la scrittura del test, ci si riconduce alla procedura gi vista dellanalisi delle variabili di ingresso (in questo caso le propriet del KosMetaInfo) individuandone le classi di equivalenza e scrivere i casi di test di conseguenza.

Database Testing (jdbc, stored procedure, jpa)


Test di classi DAO In O3-DPACS esiste un certo numero di classi DAO utilizzate per laccesso ai dati salvati nel DBMS. Tutte queste classi hanno una struttura simile tra loro ed utilizzano query SQL standard tramite i connettori JDBC. Espongono una serie di metodi pubblici che astraggono prevalentemente delle 35

SELECT ed in minor numero degli UPDATE. Ciascun metodo ottiene una connessione da un DataSource, acquisito a sua volta tramite JNDI lookup. Queste classi si aspettano quindi che a runtime ci sia un contesto JNDI in cui vi sia registrato un DataSource dal quale recuperare le connessioni al DBMS, ma tutto ci in un unit-test non possibile. necessario modificare queste classi facendo in modo che lacquisizione di una connessione possa avvenire anche in un modo differente, mantenendo al contempo il sistema esistente. Per fare questo le classi DAO sono state modificate aggiungendo un costruttore (oltre a quello preesistente senza parametri) nel quale viene passato un DataSource che la classe deve utilizzare per ottenere le connessioni. In questo modo il comportamento originale non stato intaccato ma per effettuare i test sar possibile fornire un DataSource configurato appropriatamente. Per mostrare il risultato viene riportato il caso della classe UserManager.

Il metodo privato getConnection veniva richiamato da ogni metodo pubblico per ottenere una connessione dal DataSource. Nel refactoring il DataSource una propriet privata che viene inizializzata nel costruttore. Avendo isolato le classi DAO dal resto del sistema, contesto JNDI incluso, rendendole testabili, resta il problema di dover fornire un DataSource ad ogni classe prima di verificarne i vari metodi. A questo scopo stata scritta unapposita classe astratta che se estesa, facilita la creazione di DataSource validi che vengono poi forniti alle classi sotto test. Se utilizzata, automaticamente ciascun caso di test viene eseguito N volte, dove N il numero di DBMS su cui si vuole testare. Ad ogni esecuzione, il caso verr testato su una connessione verso un DBMS diverso. Tutto ci possibile specificando nella classe astratta (JDBCTestCase) che per eseguire i test venga utilizzato il motore di JUnit per test parametrici.

In questo modo tutti i metodi contrassegnati dallannotazione @Test verranno eseguiti N volte, ciascuna avente un JDBCConnectionMetadata diverso. Questultima classe un contenitore di parametri utilizzati per la connessione al DBMS (driver, user, password, url jdbc, schema, vendor).

36

Linizializzazione del DataSource avviene in un metodo di JDBCTestCase annotato con @Before. In realt non viene creato un vero e proprio DataSource, ma viene utilizzato un Mock.

Oltre alla connessione JDBC, ne viene creata anche una seconda dedicata a DBUnit. Il risultato che qualsiasi classe estenda JDBCTestCase, avr automaticamente a disposizione un DataSource valido da poter passare allistanza sotto test, ed una connessione DBUnit per preparare il DBMS al test. Per utilizzare questa classe sufficiente estenderla, dopodich possibile utilizzare il DataSource e la connessione per DBUnit, come stato fatto nel caso di UserManagerTest:

Con questa tecnica stato possibile testare diverse classi DAO, evitando di dover ogni volta creare una connessione e un caso diverso per ogni DBMS supportato.

37

Stored Procedure Lapproccio per laccesso ai dati pi diffuso non tramite lutilizzo delle classi DAO ma tramite chiamate a Stored Procedure sparse un po ovunque nella logica di O3-DPACS. Sebbene la valutazione di questa scelta non sia oggetto di questo lavoro, lo sicuramente il problema di come testare le Stored Procedure. Queste vengono inserite in svariati punti di metodi il cui scopo principale non quello della comunicazione con il database; per questo motivo non si andr a testare quei metodi ma si andr a creare una batteria di unit-test, ciascuno dei quali avente il compito di verificare il corretto funzionamento di una stored-procedure. La totalit delle stored procedure definite in O3-DPACS sono state suddivise in categorie in base al tipo di entit su cui vanno ad operare e il tipo di operazione SQL (INSERT, UPDATE, DELETE, SELECT). Per lelenco completo e relativa classificazione si faccia riferimento alla tabella in appendice. Come per i test delle classi DAO, anche in questo caso stata creata una classe astratta che si occupa della maggior parte dei dettagli sulla creazione della connessione, molto simile alla JDBCTestCase. Tutte le classi risultanti dalla categorizzazione delle stored-procedure estendono StoredProcedureTestCase.

38

I test delle stored-procedure (SP) si differenziano da quelli semplici che utilizzano JDBC con le operazioni SQL tradizionali in quanto la sintassi delle chiamate alle SP cambia a seconda del DBMS su cui si esegue la procedura.
if (isOracle) { callableStatement = connection.prepareCall("{call getPatientInfo(?,?)}"); callableStatement.registerOutParameter(2, OracleTypes.CURSOR); callableStatement.execute(); resultSet = (ResultSet) callableStatement.getObject(5); } else if(isMySql) { callableStatement = cononnection.prepareCall("{call getPatientInfo(?)}"); resultSet = callableStatement.getResultSet(); }

Per ovviare a questo problema stata creata una classe astratta StoredProcedure che si comporta da involucro per le CallableStatement (classe Java utilizzato per eseguire le stored procedure via JDBC), le cui implementazioni per MySQL ed Oracle si occupano di utilizzare la sintassi corretta. La creazione di oggetti StoredProcedure avviene attraverso lutilizzo di una Factory, alla quale viene passata la connessione e restituisce limplementazione corretta di StoredProcedure per il DBMS in uso. 39

StoredProcedureFactory spf = new StoredProcedureFactory(connection); storedProcedure = spf.getStoredProcedure(); callableStatement = storedProcedure.prepareCall("getPatientInfo"); callableStatement.execute(); resultSet = storedProcedure.getResultSet();

Il risultato di questa infrastruttura che per testare una stored-procedure sufficiente creare una classe che estenda la classe astratta StoredProcedureTestCase, e almeno un metodo annotato con @Test in cui possibile utilizzare unistanza di StoredProcedure gi opportunamente inizializzata. Ciasuno di questi metodi @Test verr eseguito una volta per ogni tipo di DBMS configurato. JPA (Hibernate) Le interfacce JPA (Java Persistence Api) e la loro implementazione Hibernate, non sono unesclusiva di Java Enterprise, anche se la maggioranza degli utilizzatori sono applicazioni web che vengono eseguite in web container come Tomcat o JBoss. Tuttavia possibile sfruttare questa tecnologia anche all'infuori di un container. quindi possibile creare test per quello strato dell'applicazione che sfrutta JPA come astrazione della base dati. Per riuscire nell'opera di testare le classi che utilizzano direttamente le API di JPA, necessario configurare l'ambiente in cui esse andranno eseguite. In particolare le API di JPA prevedono che nel classpath vi sia un file di configurazione persistence.xml contenuta nella cartella META-INF, con all'interno la definizione delle cosiddette persistenceUnit, ciascuna delle quali descrive un set di classi che devono essere considerate come entit persistenti (classi che astraggono tabelle del database) ed una serie di configurazioni necessarie per creare connessioni al database. Per poter testare le classi e i metodi che utilizzano JPA, sar quindi sufficiente creare una 'persistence-unit' contenente tutte le informazioni necessarie per connettersi al database di test. Dalla classe di test sar cos possibile ottenere un'istanza di EntityManager (principale interfaccia messa a disposizione da JPA attraverso la quale possibile interagire con le "entit persistenti") ed effettuare tutte quelle operazioni che vengono normalmente fatte all'interno dei metodi delle classi che si vogliono testare. Rimane per un problema tipico delle applicazioni enterprise: nelle classi da testare, l'EntityManager viene ottenuto praticamente sempre tramite lookup jndi. Portando queste 40

classi fuori dal container di JBoss dove normalmente vengono eseguite, il lookup fallisce e si deve scegliere una fra le varie soluzioni studiate. In questo caso particolare si scelto di arricchire la classe da testare aggiungendo un metodo setEntityManager() con il solo scopo di permettere lassegnazione di un EntityManager creato appositamente per test, come avveniva per i DataSource.

Creazione dei test di integrazione ed MBean


I test di integrazione coinvolgono classi che hanno numerose dipendenze verso altre classi. La situazione ideale vorrebbe che queste classi secondarie siano completamente isolate dal resto dellapplicazione e ignare del loro utilizzatore (candidate ideali per unit-test). Purtroppo nella realt ci non avviene, rendendo molto difficile la verifica della classe che al suo interno fa riferimento a numerose altre classi. Se queste ultime a loro volta dipendono da altre classi, allora il test non pu che essere fatto sullambiente finale, dove lapplicazione viene eseguita solitamente. Ci sono casi in cui pi conveniente eseguire i test in questo modo, ma in altri possibile riportarsi ad una situazione favorevole alla creazione di test di porzioni di applicazione, passando attraverso un processo di refactoring. Questo lavoro pu essere anche difficile, ma se lobbiettivo viene raggiunto, allora i vantaggi ripagano sicuramente dello sforzo. Come esempio di test di integrazione reso possibile grazie al refactoring, viene riportato il caso della classe StoragePerformer. Di seguito ne viene riportato il workflow:

41

Il test di integrazione si basa sull'ipotesi che le singole componenti ivi usate siano gi state testate nei loro rispettivi unit-test (Compression e FileHasher). In questo test invece si testeranno le seguenti situazioni per coprire i vari percorsi: Test case 1: salvataggio in directory non esistente, con conseguente creazione del path (il salvataggio in directory gi esistente considerabile come un sottoinsieme di questo caso) Test case 2: l'hash viene calcolato e restituito col metodo getHash Test case 3: il file viene compresso se richiesto

Problemi e soluzioni adottate:

Dipendenza dalla classe Compression: listanza veniva recuperata da un MBean nel costruttore; questo perch listanza restituita gi stata configurata correttamente in precedenza in altri punti dellapplicazione. Per test, stato aggiunto un costruttore a StorageServer che accetta unistanza di Compression. Dipendenza dalla classe FileHasher: listanza viene creata allinterno del metodo che salva il file su disco, ma i parametri della sua configurazione vengono ricavati dal MBean di DicomServer. Per mantenere invariato questo comportamento stato creato un Mock del DicomServerMBean registrato in un MBeanServer di test. L'integrazione con il filesystem rende necessaria lesistenza di una directory disponibile sul filesystem del sistema di test. Una possibilit quella di prevedere una directory temporanea dedicata sul sistema di test. Unaltra possibilit, che quella adottata, vuole che si utilizzi la directory corrente. Questa soluzione stata preferita in quanto ha un impatto minore sul 42

sistema di test, e non ha bisogno di configurazioni. Al termine del test la cartella temporanea viene rimossa. Uno dei principali problemi che si sono presentati in questa classe nasce dal fatto che diverse configurazioni vengono prese da diversi MBean registrati in un MBeanServer (tecnologia JMX). Questo approccio si ritrova in numerose classi, per questo motivo si cercata una soluzione che potesse garantire alle classi sotto esame di trovare gli MBean richiesti, ottenendo dei parametri di configurazione opportunamente settati caso per caso. Gli MBean utilizzati in O3-DPACS sono: DicomServerMBean HL7ServerMBean StorageSCPMBean QueryRetrieveSCPMBean WorklistServiceMBean StudiesVerifierMBean MPPSSCPMBean CompressionSCPMBean ImageMaskingSCPMBean ForwarderServiceMBean AuditLogServiceMBean

Questi MBean vengono utilizzati in molteplici punti dellapplicazione per recuperare i parametri di configurazioni per i vari servizi Dicom (Storage, Query/Retrieve, Worklist), HL7, verifica degli studi, etc. Per ciascuno di essi possibile creare un Mock, che viene registrato su un MBeanServer fatto partire prima di ogni test e fermato al termine. Per semplificare questo processo si pu creare anche qui una classe astratta per JUnit.

Le classi sotto esame sono le varie StoragePerformer, StorageServer, StorageSCU etc. Queste classi si aspettano di trovare i vari MBean registrati nellMBeanServer, dai quali ottenere i parametri di configurazione. MBeanTestCase crea un MBeanServer apposito dove vengono registrati i Mock degli 43

MBean, e vengono resi recuperabili facilmente con i vari getNomeMBean. In questo modo le classi di test che estendono MBeanTestCase per preparare lambiente possono prendere il Mock dellMBean di interesse, valorizzarne i vari parametri ed essere sicuri che la classe sotto test verr eseguita leggendo i parametri configurati appositamente.

Test dei servizi


Tra i vari servizi di O3-DPACS, quello pi importante probabilmente il servizio di Storage. Questo viene utilizzato alla ricezione di immagini diagnostiche tramite il protocollo dicom. I dettagli sulla comunicazione tra client e server vengono delegati ad una libreria java apposita dcm4che. Questa libreria astrae i dettagli sulla creazione del server ed espone un interfaccia a callback dove lapplicazione pu registrarsi per gestire determinate transazioni. In dicom linvio di immagini avviene tramite la transazione C-STORE; O3-DPACS programmato in maniera tale che alla ricezione di un immagine venga richiamato, tramite callback di dcm4che, il metodo doCStore della classe StorageServer. Il test di questo metodo consente di verificare la logica di business relativa alla ricezione di file dicom. Normalmente per effettuare dei test necessario avviare lapplicazione al completo. Lobbietivo quindi evitare tutto ci isolando il pi possibile la classe in esame. Il primo passo identificare le dipendenze. Dallanalisi della classe sono state evidenziate le seguenti dipendenze: 1. 2. 3. 4. 5. 6. 7. 8. Nota-1 GlobalConfigurationLoader una classe che espone un metodo statico getConfigParam per recuperare dei parametri di configurazione dal DBMS. Per rendere StorageServer indipendente da questa classe e quindi dalla presenza del DBMS, stato effettuato un refactoring di GlobalConfigurationLoader: stata creata uninterfaccia ConfigurationLoader la quale espone il metodo getConfigParam, e GlobalConfigurationLoader diventata una implementazione che recupera i dati dal DBMS. Per il test di StorageServer, viene passato un Mock dellinterfaccia. Nota-2 Di CompressionSCP viene richiamato soltanto il metodo statico getCompressionTransferSyntax, che a sua volta richiama un metodo di DicomDbDealer. stata quindi eliminata questa dipendenza a favore della chiamata diretta a DicomDbDealer. Nota-3 Di ImageMaskingSCP vengono usati i metodi isImageElaborationEnabled, getMaskTags, getMasks. I primi due richiamano i metodi di DicomDbDealer, quindi sono stati eliminati in favore a chiamate 44 Anonimizer: aggiunto ai parametri del costruttore GlobalConfigurationLoader: vedi nota-1 StorageSCPMbean: gi presente nei parametri del costruttore DicomStorageDealerLocal: aggiunto ai parametri del costruttore CompressionSCP: vedi nota-2 DicomStorageDealer: aggiunto ai parametri del costruttore ImageMaskingSCP: vedi nota-3 DicomDbDealer: aggiunto ai parametri del costruttore

dirette a questultimo. Il terzo metodo un metodo statico al cui interno non ci sono ulteriori dipendenze, quindi stato mantenuto. Il costruttore della classe StorageServer originale ha 1 solo parametro. Quello creato appositamente per consentirne il test ne ha 5. Non sono stati riscontrati casi con un numero ancora maggiore di dipendenze, per questo motivo si ritenuto che esplicitare questi parametri nel costruttore sia una soluzione accettabile, motivazione avvalorata anche dal fatto che rende pi evidente quali sono le oggetti utilizzati di cui si deve tener conto. In questo modo stato ottenuto un livello di isolamento sufficiente per poter sottoporre la classe a test allinfuori del container J2EE.

Per testare il metodo doCStore necessario creare un server dicom apposito sfruttando le librerie dcm4che, e registrare listanza di StorageServer quale ascoltatore (listener) per transazioni di tipo C-STORE. A questo punto possibile inviare unimmagine dicom al server attraverso una seconda classe creata appositamente.

45

Il test degli altri servizi dicom segue la stessa struttura, quello che cambia il tipo di transazione effettuata dal DicomClient e listanza della classe sotto esame che viene registrata nel DicomServer come classe da chiamare alla ricezione di quella determinata operazione. Lo stesso approccio da utilizzarsi anche per i servizi HL7. Cambiano le librerie, ma il principio di utilizzo lo stesso. Anche queste librerie prevedono la creazione di un Server HL7 al quale viene registrata la classe che si occupa di gestire i vari messaggi. Le classi da testare hanno lo stesso nome della transazione, quindi per i messaggi ADT^A08 (aggiornamento anagrafica paziente), si testa la classe ADTA08 tramite la classe di test ADTA08Test, la quale crea il client e server HL7 e gestisce linvio del messaggio e successiva verifica dei risultati.

46

47

Automazione
Per lautomazione dellesecuzione dei test stato scelto di usare Jenkins. Questo uno dei server di Continuous Integration (CI) esistenti e tra i pi utilizzati; lintegrazione continua una pratica di sviluppo del software dove le modifiche al codice vengono integrate frequentemente. Ad ogni integrazione dovrebbe seguire un build automatico (compilazione e impacchettamento dei vari jar, war, ear), eseguito su un server apposito, verificato con lesecuzione di tutti i test definiti per il progetto. I requisiti per mettere in pratica questa metodologia sono:

1. 2. 3. 4. 5.

codice su repository (SVN, CVS, git) codice con commit frequenti sul trunk (commit di ogni issue) commit di codice sempre "funzionante" tool di building automatico (ant, maven, make) server di CI (Jenkins, Cruise Control, Apache Continuum, etc.)

Il processo di sviluppo di O3-DPACS soddisfa pienamente i primi tre punti. Il quarto punto parzialmente implementato (esistono e vengono utilizzati gli script di build dei vari pacchetti, che va integrato con uno script per la compilazione dei test e della loro esecuzione). Nel quinto punto entra in gioco Jenkins.

Script di build ed esecuzione automatici


O3-DPACS utilizza script Ant per la compilazione e impacchettamento dei sorgenti. Di conseguenza stato utilizzato anche per la compilazione delle nuove classi di test. Il task per la loro compilazione stato integrato nello script preesistente, affiancandolo a quelli originali. Per lesecuzione dei test invece, stato creato uno script ant apposito TestRunner.xml con i seguenti task:

Il task Compile and run un task che dichiara solo dipendenze e fa scattare gli altri test nellordine indicato nel grafico. 48

Il primo task chiamato Prepare for tests che copia i file di configurazione necessari ai test (dati di test, file xml, e file di propriet per le connessioni ai DBMS). Compile core tests compila i sorgenti dei test, ma non prima di aver compilato i sorgenti dellapplicazione. Infine lancia il task che esegue tutti i test JUnit presenti nei sorgenti.

Utilizzo di Jenkins
Lobbiettivo finale riuscire a configurare il server di CI in maniera tale che ad ogni commit sul trunk dellSVN, vengano automaticamente scaricati gli ultimi sorgenti, compilati, ed eseguiti tutti i test, creando un report visualizzabile da interfaccia web. Jenkins viene distribuito come un singolo file war (applicazione web Java Enterprise), che pu essere eseguito allinterno di un container J2EE, ma pu essere anche eseguito come applicazione stand-alone in quanto nella distribuzione gi compreso un mini container J2EE. Ovviamente questo pone delle limitazioni alle possibilit di configurazione, ma per il momento pi che sufficiente. Per far partire lapplicazione sufficiente eseguire il comando:
java jar jenkins.war

In questo modo viene avviato un server web con lapplicazione al suo interno. La configurazione avviene completamente tramite interfaccia web. I passi necessari per automatizzare il build e i test di O3-DPACS sono: Creare un Job Jenkins per O3-DPACS Configurare il repository SVN in modo tale che prenda i sorgenti dal trunk del progetto Specificare gli script ant per il build dellapplicazione Aggiungere il secondo script ant per lesecuzione dei test

Il job cos configurato in grado di compilare applicazione e test, ed eseguire questi ultimi. Per fare in modo che questo job venga eseguito ad ogni commit sul trunk, necessario aggiungere un poll sul repository SVN che controlli periodicamente se ci sono modifiche dallultimo controllo. Per ottenere il report dei test stato necessario modificare lo script Test Runner affinch generi un report XML, dopodich da Jenkins sufficiente indicare la directory dove vengono salvati i report per fare in modo che questi vengano caricati nellinterfaccia web.

49

Conclusioni
Risultati ottenuti
Il progetto O3-DPACS stato arricchito da una serie di strumenti che consentono e facilitano la scrittura di unit-test per diverse componenti dellapplicazione che altrimenti richiederebbero lesecuzione dellintera applicazione in un ambiente completamente configurato. Le componenti principali sono: Classi DAO Stored procedure Classi che accedono al DBMS tramite Hibernate Classi che utilizzano gli MBean di JMX Servizi Dicom e HL7

Sono inoltre stati affrontati gli ostacoli pi ricorrenti emersi nel tentativo di testare classi con dipendenze, analizzando i vari problemi e individuando soluzioni comuni come il refactoring delle classi sfruttando il pattern di programmazione della Dependency Injection. Ci si avvalsi di ulteriori tecniche per aumentare il disaccoppiamento tra moduli ricorrendo a classi Stub e Mock e relativi framework. Affianco allattuale processo di testing del software di O3-Enteprise, sono stati integrati gli unit-test con lintroduzione di JUnit come framework di base e sono stati creati dei test veri e propri su parti eterogenee del codice. Nel farlo, sono state utilizzate tecniche di software testing quali il partizionamento delle variabili di input in classi di equivalenza e copertura intelligente del codice tramite esplorazione dei grafi. stata infine proposta una soluzione funzionante per adottare la tecnica della Continuous Integration che permette lesecuzione automatica dei test ad ogni modifica del codice versionato su SVN.

Confronto
Dellinsieme di tutti i test creati, soltanto alcuni possono essere considerati equivalenti a dei test preesistenti. Perci solo di questi stato possibile fare un confronto diretto in termini di tempo desecuzione. Dalle interviste fatte agli sviluppatori di O3-DPACS (5 persone) sono stati ricavati dei dati soggettivi sui tempi desecuzione da cui sono emersi i seguenti dati. Tipo di test Unit Test Prima 2-15 minuti Dopo Immediati ed automatici

Functional Test 15-60 minuti

stato possibile distinguere due categorie di test a seconda del tempo necessario per la loro esecuzione. Quelli che ho qui riportato come Unit Test sono quelli pi semplici e veloci, la cui verifica non richiede pi di 15 minuti. Ve ne sono altri che sono pi complessi e che possono richiedere fino a 60 minuti. In entrambi i casi con la nuova infrastruttura la loro esecuzione immediata ed automatica. 50

Glossario
HL7: standard internazionale che definisce un protocollo di comunicazione tra sistemi informativi sanitari DICOM: standard per la diagnostica di immagini IHE: iniziativa internazionale che promuove linteroperabilit tra sistemi informativi sanitari attraverso lindicazione di standard e processi comuni per facilitare lintegrazione tra software ospedalieri eterogenei. XDS-I: profilo di IHE per la condivisione di puntatori dicom (Key Object Selection, KOS) tra sistemi informativi diversi. JMX: Java Management Extension, tecnologia Java che consente la pubblicazione di interfacce per la gestione delle applicazioni java. Le interfacce sono chiamate MBean e vengono registrate in un MBeanServer con un nome univoco. Tramite lMBeanServer possibile invocare i metodi esposti dagli MBean, ai quali si possono anche passare dei parametri, consentendo cos la configurazione a runtime di applicazioni tramite uninterfaccia standard. JNDI: una API java per servizi di directory quali LDAP, RMI, DNS. Nei container Java Enterprise utilizzato per registrare risorse messe a disposizione dal container per le applicazioni che vi sono ospitate. Tra queste risorse ci sono i DataSource per la connessione ai DMBS. SVN: Subversion, sistema per la gestione del versionamento di codice sorgente.

Bibliografia
[1] [2] [3] [4] UNI EN ISO 13485:2003 UNI CEN ISO/TR 16969 (guida per lapplicazione della 13485) CEI EN 60601-1-4 norme generali per la sicurezza (sistemi elettromedicali programmabili) CEI EN 62304 Norma (Software per dispositivi medici processi relativi al ciclo di vita del software) [5] Manuale per la classificazione dei dispositivi medici borderline
http://ec.europa.eu/health/medical-devices/files/wg_minutes_member_lists/borderline_manual_ol_en.pdf

[6] M. Fowler, D. Rice, M. Foemmel, E. Hieatt, R. Mee, R. Stafford Patterns of Enterprise Application Architecture - Addison-Wesley Professional, 2002 [7] P. Tahchiev, F. Leme, V. Massol, G. Gregory JUnit in Action, Second Edition - Manning Publications, 2010 [8] Graham Bath, Judy McKay The Software Test Engineer's Handbook - Rocky Nook, 2008

51

Appendice

o3 Consortium & o3 Enterprise Projects Management Displaying 48 issues at 21/Feb/12 09:32:54 AM. Issue Type SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug Summary Sometimes the second instance of a study is not stored in fastestAccess Change class Dbms to include the chance to use both Oracle and non-Oracle datasources Oracle out of resources: ORA-01000 maximum open cursors exceeded Correct getRetrievalInfo under Oracle On Oracle: querying by study date brings an Exception [StorageCommitment] Unable to retrieve information, the queries executed on PACS DB are wrong Problems in inserting records in "Images" table [CMove] The connection is released before the ack by the SCP Cancel and Abort seem not to work properly C-FIND: when asking ProcedureCodeSeq a NullPointerException is thrown [MoveServer] If at least patient id is specified, the system throws an Exception Correct getDataForCMove and getRetrievalInfo C-FIND: Check that Required fields are supported correctly Presentation Contexts may not be handled properly Move Dealer does not retrieve results by called by WADO, when this is called using private ip O3-DPACS ignores c-cancel requests CalledAET is never checked [Query] Ranged matching on the studies doesn't work (in modality "from Date") [ForwarderService] Service locks down after first run The timer which checks whether the storage media is running out of space does not consider the media type Services don't start from web if autostart is not used [Dicom Server and HL7 Server] The configuration isn't reloaded correclty Forwarder Service doesn't work on Oracle Compressing and storing images with jai not properly installed, fails. A success status is however returned. Storage media could not save the image Worklist search on intervals are not cross-dbms Worklists: management of timestamps in other DBs [Worklist] Some Modality SCU refuse the worklist Not all Worklist tags are treated correctly [WorkList] if the third party view doesn't contain all aspected comlumns, the worklist server throw up an exception. [Worklist] The system put SPSStartDate in the PatientBirthdate Dicom Tag ADT A40 should not update the target information worklist storage service dicom Area database

52

SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug

Single quotes in Patient Id do not allow proper storage Multiframe generation: wrong series UID in generated file Some exceptions are fake and need to be removed Memory consumption too high when processing hash If Procedure Code Sequence is requested, possible nullpointer exception If more than two MBeanServers are registered on MBeanServerFactory ambiguos log message are sended. [Image Masking] If there is a difference between TS saved in FileMetaInfo and TranferSyntax dicom tag image masking propagates this discrepany and dcm4che lib throw an Exception The creation of ATNA message has not to be blocking, Better management of presentation context and transfersyntax FORWARDER: When deleting a study, update the available bytes in PhysicalMedia installation following the instructions fails IMAGEIO Bug: Use a jai_imageio.jar released later than Dec 4 2007 exception on duplicated ProtocolCodeSequence... if there isn't any Node config in the DB the o3-dpacs servers note start. If the node has the compressione enabled but does not accept compressed images, O3-DPACS should not compress them when sending. when Patient ID is the same between two patients, O3-DPACS should not accept the study when Anonymization is enabled, query with teh patient first name, last name and birdth day should be disabled

SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug SYS Bug

Generated at Tue Feb 21 09:32:54 CET 2012 by Giacomo Petronio using JIRA 4.2.4-b591#591.
6 - Estrazione dei bug del sistema e suddivisione per aree

Stored Procedure ADDNEWPATIENT ADDNEWSTUDY ADDSTUDYTRACKING ARCHIVESTUDY COMPLETEFORWARDPROCESS COMPLETEMFCREATION COMPLETEOLDSTUDIES COMPLETESTUDY DELETEINSTANCES DOCHANGEPASSWORD GETALLSERIESFROMSTUDY GETALLSTUDIESFROMPATIENT GETDATAFORCMOVE GETEMAILCONFIGURATIONS

Tipo insert insert insert update

Categoria patient study availability media forward mf availability availability

Classe di test InsertPatientStoredProceduresTest StudyStoredProceduresTest AvailabilitySPTest MediaSPTest ForwardSPTest MultiFrameSPTest AvailabilitySPTest AvailabilitySPTest InstancesSPTest ConfigurationStoredProceduresTest SeriesStoredProceduresTest StudyStoredProceduresTest MoveSPTest EmailStoredProceduresTest

delete

instance conf

select select

series study move

select

email

53

GETEMAILFROMROLEFK GETEVENTEMAIL GETGLOBALCONFIGURATION GETINSTANCES GETORCREATEPATIENT GETPASSWORDCONSTRAINTS GETPATIENTBASICINFO GETPATIENTBASICINFOAN GETPATIENTHL7CONTEXTSTUDIES GETPATIENTINFO GETPATIENTINFOFORRECO GETRETRIEVEALINFO GETSERIESBASICINFO GETSERIESINSTANCES GETSTUDIESFORMOVEVISIT GETSTUDIESTOVERIFY GETSTUDYBASICINFO GETSTUDYBASICINFOAN GETSTUDYINFOFORRECO GETURLTOINSTANCE GETUSERFOREMAIL GETVERIFIEDSTUDIESINSTANCES IAINSERTCORRECTSTUDIES IAINSERTCORRECTSTUDY IARECONCILEWRONGSTUDY IASWAPPATIENTASSOCIATION INITSCHEDULEPROCESS INSERTHL7ALLERGY INSERTHL7OBSERVATION INSERTKOSRELATIONSHIP INSERTSTUDYTOVERIFY INSERTVERIFIEDDATE ISCONVERTED ISUSERAUTHENTICATED MAPSETTINGFROMMEDIATOSTUDY MOVESTUDY MOVESTUDYTOPATIENT

select select select select insert

email email conf instance patient conf

EmailStoredProceduresTest EmailStoredProceduresTest ConfigurationStoredProceduresTest InstancesSPTest InsertPatientStoredProceduresTest ConfigurationStoredProceduresTest SelectPatientStoredProceduresTest SelectPatientStoredProceduresTest SelectPatientStoredProceduresTest SelectPatientStoredProceduresTest SelectPatientStoredProceduresTest OthersSPTest

select select select select select

patient patient patient patient patient

select select select select select select select

series instance move studyverifier study study study media

SeriesStoredProceduresTest InstancesSPTest MoveSPTest StudyVerifierSPTest StudyStoredProceduresTest StudyStoredProceduresTest StudyStoredProceduresTest MediaSPTest EmailStoredProceduresTest StudyVerifierSPTest AvailabilitySPTest AvailabilitySPTest AvailabilitySPTest AvailabilitySPTest OthersSPTest

select select

email studyverifier availability availability availability availability

insert insert insert insert update

patient patient

InsertPatientStoredProceduresTest InsertPatientStoredProceduresTest OthersSPTest

studyverifier studyverifier mf conf

StudyVerifierSPTest StudyVerifierSPTest MultiFrameSPTest ConfigurationStoredProceduresTest OthersSPTest

move move

MoveSPTest MoveSPTest

54

MOVEVISIT REMOVEKOSRELATIONSHIP RESETSTUDIESTORECONCILE RETRIEVEVISITPERSTUDY ROLLBACKCONVERSIONDATE SELECTFORWARDEDSTUDIES SELECTSTUDIESTOARCHIVE SELECTSTUDIESTOFORWARD SETEXPIRATIONDATE UPDATEFORWARDEDSCHEDULE UPDATEFORWARDSCHEDULE UPDATEPASSWORD UPDATEPATIENTINFORMATION UPDATEPHYSICALMEDIASIZE UPDATESCHEDULE select select select update update update update update update update select

move

MoveSPTest OthersSPTest

study study mf forward forward forward conf forward forward conf patient media forward

StudySPTest StudySPTest MultiFrameSPTest ForwardSPTest ForwardSPTest ForwardSPTest ConfigurationStoredProceduresTest ForwardSPTest ForwardSPTest ConfigurationStoredProceduresTest UpdatePatientStoredProceduresTest MediaSPTest ForwardSPTest

Tabella 7 - Classificazione delle stored procedure

55