Il test
Tecniche e metodologie di test Qualità del software Sommario Introduzione al test Teoria del test Tecniche e metodologie di test Qualità del software Metriche per la stima di qualità Test e qualità dell’applicazione sviluppata Conclusioni
Cos’è il test? Insieme di misure di controllo utili a verificare la qualità di un software. Esistono diversi livelli di test.
Scopo Rilevare gli errori
Teoria del test
Un caso di test è un elemento di D. Definizioni Dato un programma P, D ed R siano i domini di ingresso e uscita, P definisce una relazione tra D ed R. Un caso di test è un elemento di D. Un insieme di test T è un insieme finito di casi test, quindi un sottoinsieme finito di D. P è corretto per T se risulta corretto per tutti gli elementi di T. Si dice che T ha avuto successo per P. Un insieme di test si dice ideale se, tutte le volte che P è scorretto, esiste un d e T tale che P risulta scorretto per d. Un test ideale mostra sempre l’esistenza di un errore in un programma, se tale errore esiste. Per un programma corretto qualunque insieme di test risulta ideala. Se T è un insieme ideale e T ha successo per P allora P è corretto.
Criterio di selezione del test Definizione Teoria del test Criterio di selezione del test Definizione Dato un programma P e le relative specifiche S, un criterio di selezione del test specifica le condizioni che devono essere soddisfatte da un insieme T di casi di test. Es. il criterio specifica che tutti gli statements nel programma devono essere eseguiti almeno una volta durante il test allora un insieme di casi di test T soddisfa il criterio per un programma P se l’esecuzione di P su T assicura che ogni statement in P sia eseguito almeno una volta.
Proprietà del criterio di test: Affidabilità Validità Definizione Un criterio di test è affidabile se tutti gli insiemi (di casi di test) che soddisfano il criterio individuano gli stessi errori Un criterio di test è valido se per ogni errore nel programma c’è un qualche insieme che soddisfa il criterio che rileva l’errore.
Indecidibilità della correttezza dei programmi Tesi di Dijkstra Indecidibilità della correttezza dei programmi Tesi di Dijkstra: il test di un programma può rilevare la presenza di malfunzionamenti, ma non dimostra la loro assenza. Il test non può dare garanzia completa di correttezza dovendo essere limitato il numero dei casi di test per ragioni legate al costo Teorema di Goodenough e Gerhart: Un criterio di test è affidabile se tutti gli insiemi (di casi di test) che soddisfano il criterio individuano gli stessi errori Corollario Teorema di Howden: dato un programma P, non esiste un algoritmo che generi un test ideale finito, cioè selezionato da un criterio affidabile e valido
Assiomi di Weyunker Assioma di applicabilità Per ogni programma (con relative specifiche) esiste un insieme di test T che soddisfa il criterio Per tutti i programmi è possibile avere un insieme di casi di test che soddisfa il criterio Assioma di antiestensionalità Esistono due programmi P e Q, che soddisfino entrambi le stesse specifiche, tali che un set di test T soddisfa il criterio per P ma non per Q. La struttura del programma ha un ruolo importante nel decidere i casi di test Assioma di antidecomposizione Esiste un programma P ed un suo componente Q tale che un set di test T soddisfa il criterio per P e T’ è l’insieme dei valori che le variabili possono assumere fornendo Q per qualche caso di test In T e T’ non soddisfa il criterio per Q. Se il criterio è soddisfatto per l’intero programma non è detto che sia soddisfatto per ogni suo componente. Assioma di anticomposizione (duale dell’assioma di anticomposizione) Esistono due programmi P e Q tali che T soddisfi il criterio per P e gli output di P per T (rappresentati da P(T)) soddisfano il criterio per Q, ma T non soddisfa il criterio per P;Q. Se il criterio soddisfa separatamente le parti P e Q
Molti dei criteri sono essi stessi indecidibili: Osservazioni Molti dei criteri sono essi stessi indecidibili: Non è decidibile se dato un dato insieme di test soddisfi i criteri o se esista un insieme di test che li soddisfi. Ciò significa che non è possibile automatizzare la soluzione del problema.
Riferimenti bibliografici Teorema di Goodenough e Gerhart: J. Goodenough and S.L. Gerhart, “Towards a theory of test data selection”, IEEE Transactions on software engineering, SE-1:156-173,1975. Tesi di E.W. Dijkstra (1969): “Program testing can be used to show the presence of bugs, but never to show their absence!”. J.N. Buxton and B. Randell, eds, Software Engineering Techniques, April 1970, p. 16. Report on a conference sponsored by the NATO Science Committee, Rome, Italy, 27–31 October 1969. Teorema di Howden: W.E.Howden,” Symbolic Testing and the DISSECT Symbolic Evaluation System”, IEEE TRANSACTIONS ON SOFTWARE ENGINEERING, VOL. SE-3, NO. 4, JULY 1977. Assiomi di Weyunker E.J. Weyunker ,"Axiomatizing software test data adequacy”, IEEE transaction on software engineering, 12(12):1128-1138, Dec. 1986
Livelli di test
test di unità test di integrazione test di sistema Livelli di test test di unità test di integrazione test di sistema test di accettazione test di regressione test di unità - mirato alla correttezza degli algoritmi. test di integrazione - mirato alla correttezza delle interfacce. test di sistema – mirato a garantire l’affidabilità, sicurezza e le prestazioni di un sistema software. test di accettazione - imposto dal cliente, verifica che il programma soddisfi i requisiti richiesti. test di regressione - verifica che non si siano introdotti errori in versioni successive
Test di unità Controllare separatamente le diverse parti di un modulo di un programma, per rilevare gli errori e migliorarle durante la codifica
Test d’integrazione I moduli sono integrati in sottosistemi più grandi i quali a loro volta sono integrati in sistemi più complessi. Il test d’integrazione si effettua durante l’integrazione e consiste in un controllo delle interfacce dei moduli integrati
Test di sistema Consiste nel verificare che le specifiche di progetto di un programma siano state soddisfatte e i miglioramenti apportati
Test di accettazione Imposto dal cliente per verificare che il programma soddisfi le richieste del cliente stesso
Verifica che non si siano introdotti errori in versioni successive Test di regressione Verifica che non si siano introdotti errori in versioni successive
Processo di test Test plan: Documento sui casi di test: Test report: descrive tutte le attività di test che devono essere eseguite le risorse da allocare le linee guida Le condizioni di test per ogni singolo modulo Le modalità con cui i vari moduli dovranno essere integrati Documento sui casi di test: Contiene l’elenco di tutti i differenti casi di test I risultati ottenuti Le uscite che ci si aspettava Test report: Una serie di casi di test Il risultato dell’esecuzione del codice ottenuto applicando i casi di test Error report: Descrive gli errori che si sono presentati Le azioni intraprese per rimuoverli Test Plan: sono descritte tutte le attività di test da eseguire, le risorse da allocare e le linee guida. Una volta eseguiti i test sulle diverse unità, viene prodotto un documento contenente l’elenco di tutti i differenti casi di test, i risultati ottenuti e le uscite che ci si aspettava. Test report: contiene una serie di casi di test ed il risultato dell’esecuzione del codice, ottenuto applicando questi casi di test. Error report: descrive gli errori che si sono presentati e le azioni intraprese per rimuoverli.
2. Principali tecniche e metodologie di test Ogni fase del ciclo di vita si conclude con un’attività di verifica Molte di queste attività nelle prime fasi di sviluppo si basano su valutazioni umane e non permettono di rilevare tutti gli errori: Il test si occupa dell’ultima fase del ciclo di vita del software e ha la responsabilità di rilevare qualsiasi tipo di errore Esistono errori di diverso tipo, che possono avvenire durante qualunque fase, per cui esistono diversi livelli di test: ognuno aspira a testare aspetti differenti del sistema
Tecniche di test Analisi statica: Analisi dinamica: Analisi formale: Processo di valutazione di un sistema o di un suo componente basato sulla sua forma, struttura contenuto, documentazione senza che esso sia eseguito: Ispezioni, revisioni, recensioni, analisi data flow Analisi dinamica: Processo di valutazione di un sistema software o di un suo componente basato sull’osservazione del suo comportamento in esecuzione (tipicamente il test) Analisi formale: Processo che usa rigorose tecniche matematiche per l’analisi di algoritmi Adoperato soprattutto per la verifica del codice e dei requisiti specie se non sono specificati con linguaggi formali Analisi statica: processo di valutazione di un sistema o di un suo componente basato sulla sua forma, struttura, contenuto, documentazione senza che esso sia eseguito. Esempi sono: revisioni, ispezioni, recensioni, analisi data flow Analisi dinamica: processo di valutazione di un sistema software o di un suo componente basato sulla osservazione del suo comportamento in esecuzione. Tipicamente con il termine testing ci si riferisce all’analisi dinamica Analisi formale usa rigorose tecniche matematiche per l’analisi di algoritmi. E’ usata soprattutto per la verifica del codice e dei requisiti specie quando questi sono specificati con linguaggi formali.
Tecniche di analisi statica Analisi statica in compilazione Code reading Control flow analysis Data flow analysis Code inspections or reviews Esecuzione simbolica Analisi statica in compilazione consiste nel generare delle Cross Reference List, ossia dei rapporti, molto utili nelle analisi successive del codice, per l’individuazione di anomalie non rilevabili dal compilatore. Code reading è una tecnica di controllo che consiste in un’attenta lettura del codice per individuare errori e/o discrepanze con il progetto. Control Flow Analysis Il codice è rappresentato tramite un grafo, il grafo del flusso di controllo (Control Flow Graph - CFG), i cui nodi rappresentano statement (istruzioni e/o predicati) del programma e gli archi il passaggio del flusso di controllo. Il grafo è esaminato per identificare ramificazioni del flusso di controllo e verificare l’esistenza di eventuali anomalie, quali codice irraggiungibile e non strutturazione. Data flow analysis analizza l’evoluzione del valore delle variabili durante l’esecuzione di un programma, permettendo di rilevare anomalie. Code inspections o reviews sono delle riunioni formali cui partecipa un gruppo di persone tra cui almeno una del gruppo di sviluppo. Il codice è esaminato linea per linea e i partecipanti fanno commenti e/o annotazioni. Esecuzione simbolica: il programma non è eseguito con i valori effettivi ma con valori simbolici dei dati di input. L’esecuzione procede come un’esecuzione normale ma non sono elaborati valori bensì formule formate dai valori simbolici degli input. Gli output sono formule dei valori simbolici degli input.
Tecniche di analisi dinamica Black Box Testing o Testing Funzionale: Testa il programma per esaminare come dovrebbe essere È fondato sull’analisi degli output generati dal sistema o dai suoi componenti in risposta ad input definiti sulla base della sola conoscenza dei requisiti specificati del sistema o di suoi componenti Analizza requisiti funzionali, specifiche, decomposizioni funzionali, requisiti di performance White Box Testing o Testing Strutturale: Testa il programma per esaminare com’è È fondato sulla definizione dei casi di test, degli input associati e dell’oracolo, sulla base della conoscenza della struttura del software ed in particolare del codice Tipi di dati, Dati, strutture di controllo, Control flow, Data flow, Callgraph, Dominanze, dipendenze Testing funzionale: é fondato sull'analisi degli output generati dal sistema in risposta ad input definiti sulla base della sola conoscenza dei requisiti specificati del sistema. Testa il programma per esaminare come dovrebbe essere. Testing strutturale: è fondato sulla definizione dei casi di test, degli input associati e dell'oracolo, sulla base della conoscenza della struttura del software ed in particolare del codice. Testa il programma per esaminare com’è.
Tecniche di testing funzionale … cosa testare Funzionalità esterne (visibili all’utente e definite dai requisiti e dalle specifiche) Funzionalità interne ( non visibili all’utente e definite dal progetto di high e low level) …. partendo da Definizione Funzionalità: Insieme dei dati d’ingresso, dati d’uscita, precondizioni e postcondizioni Funzionalità esterne: funzionalità visibili all’utente e definite dai requisiti e dalle specifiche Funzionalità interne: funzionalità non visibili all’utente e definite dal progetto di high e low level Definizione Funzionalità: dati di ingresso, dati di uscita, precondizioni, postcondizioni
Effettuare un test È necessario conoscere il comportamento atteso per poterlo confrontare con quello osservato e per poter definire l’oracolo L’oracolo conosce il comportamento atteso per ogni caso di test e può essere: Umano, se si basa sulle specifiche o sul giudizio Automatico se è generato dalle specifiche formali Lo stesso software scritto da altri Una versione precedente ( test di regressione) Ogni funzionalità deve essere eseguita almeno una volta e per ogni funzionalità bisogna effettuare un numero di esecuzioni dedotte dai dati di ingresso e di uscita, da precondizioni e postcondizioni
Selezionando precondizioni e postcondizioni Test case Vanno effettuati una volta definito il dominio dei dati di I/O selezionando: Valori in posizione centrale Valori ai bordi Valori speciali Selezionando precondizioni e postcondizioni Positivi Negativi Neutrali
Documentazione dei test I test eseguiti vanno documentati producendo una check list: funzionalità TC Input Output Precond Postcond Priorità esito
Definizione della check list Si parte dall’analisi dei documenti che definiscono i requisiti e la specifica Si estraggono le funzionalità esterne ed altre features, dati di I/O, pre e post condizioni, definizione priorità, nomi dei test Si procede con l’analisi dei documenti di progetto di high e low level e con l’estrazione delle funzionalità interne, dati di I/O, pre e post condizioni, definizione priorità e nomi dei test Si definiscono i test case e le procedure di test Si genera la tabella di copertura (o dei test case) e la tabella delle procedure
Tecniche di testing strutturale Si basa sulla scelta dei dati di test in base alla struttura del codice testato Per effettuare una completa analisi strutturale si applicano i seguenti criteri criterio di inadeguatezza Se parti significative della struttura del programma non sono coperte il test è inadeguato criteri di glass box (criteri di copertura strutturale del flusso di controllo) Copertura delle istruzioni (statement coverage) Copertura delle diramazioni (edge coverage) Copertura delle condizioni (condition coverage) Copertura dei cammini (path coverage) criterio di inadeguatezza: se parti significative della struttura del programma non sono coperte, il testing è inadeguato. criteri di glass box: coincidono con i criteri di copertura strutturale del flusso di controllo
Copertura Un insieme di casi di test (input data) tale che gli oggetti di una definita classe (strutture di controllo, istruzioni, predicati) siano attivati almeno una volta nell’esecuzione dei casi di test.
Metodo di copertura delle istruzioni Si seleziona un insieme T di dati di test per cui ogni istruzione viene eseguita almeno una volta da qualche dato di T. In questo caso, fissato il criterio, si cerca di trovare il T di cardinalità minima, che soddisfa il criterio. Intuitivamente, il criterio di copertura delle istruzioni si basa sull’osservazione che non è possibile individuare un errore se la parte del programma che contiene l’errore e che genera il fallimento non viene eseguita. E’ pertanto necessario sforzarsi di coprire completamente l’insieme delle istruzioni.
Metodo di copertura delle istruzioni Si seleziona un insieme T di dati di test tali che ogni istruzione viene eseguita almeno una volta da qualche dato di T. Non è possibile individuare un errore se la parte del programma che contiene l’errore e che genera il fallimento non viene eseguita
Metodo di copertura delle diramazioni Si seleziona un insieme T di dati di test tale che ogni diramazione del flusso di controllo viene selezionata almeno una volta da qualche elemento di T.
Metodo di copertura delle condizioni Si seleziona un insieme T per cui si percorre ogni diramazione e tutti i possibili valori dei costituenti della condizione che controlla la diramazione sono esercitati almeno una volta.
Metodo di copertura dei cammini Si seleziona un insieme T che attraversa tutti i cammini del programma. Problema: presenza di un numero infinito di cammini e/o non percorribili Soluzione: numero finito di cammini eseguibili Trovare un insieme di test case che assicuri l’attraversamento almeno una volta di almeno un cammino per ogni classe
Metodo di copertura dei cammini minimi Metodi fondati sui grafi di controllo Metodo dei cammini esemplari Metodo dei cammini linearmente indipendenti (McCabe) Metodi data flow: Garantire il test delle variabili e dei loro usi Nei metodi basati sul grafo di controllo, l’insieme (o un sottoinsieme definito) dei cammini del grafo di controllo viene partizionato in un numero finito di classi di equivalenza. Il criterio di copertura consiste in un insieme di test case che assicuri l’attraversamento almeno una volta di almeno un cammino per ogni classe. Metodo dei cammini esemplari: Si consideri il numero di cammini elementari (ossia cammini privi di circuiti ) e totali di GfC(P). Allora, con questo metodo, le classi di cammini considerate comprendono un cammino elementare totale e tutti i cammini che da esso differiscono unicamente per il numero di attraversamenti di circuiti che sul cammino giacciono. Metodo dei cammini linearmente indipendenti: un cammino si dice indipendente se introduce almeno un nuovo insieme di istruzioni o una nuova condizione; in un CfG un cammino è indipendente se attraversa almeno un arco non ancora percorso. L’insieme di tutti i cammini linearmente indipendenti di un programma formano i cammini di base; tutti gli altri cammini sono generati da una combinazione lineare di quelli di base. Dato un programma, l’insieme dei cammini di base non è unico. Nei metodi fondati sul data flow (flusso di dati), prima del flusso di controllo, per specificare meglio i casi di test, si esegue un’analisi delle informazioni su dove le variabili vengono definite e su dove tali definizioni debbano essere utilizzate.
Mutanti: testing strutturale Mutation testing Mutanti: testing strutturale Obiettivo: garantire che, durante la fase di test, ogni mutante (variazione del programma) produca un’uscita diversa dall’uscita del programma originale. Si creano tanti “mutanti” del programma, apportando delle semplici modifiche al programma originale. Obiettivo: si richiede che l’insieme dei casi di test sia in grado di distinguere il programma originale dai suoi mutanti.
Test di software OO Concetti di OO: Test di software OO: Modularizzazione Information hiding Tipi di dati astratti Progettazione per il cambiamento Ereditarietà Polimorfismo Binding dinamico Test di software OO: Shadow invocations Polimorfismo e dynamic binding Genericità
Ereditarietà Genericità Polimorfismo Flattering inheritance Test incrementale Genericità Data-flow analysis Polimorfismo
Problemi Ereditarietà: i metodi sono riutilizzati in classi diverse Information hiding: gli oggetti, avendo uno stato violano le ipotesi di un comportamento funzionale Shadow invocations: i metodi possono essere invocati implicitamente (problemi relativi al conteggio delle percentuali di copertura) Polimorfismo e dynamic binding: i metodi invocati non possono essere identificati staticamente Genericità: le classi generiche devono essere istanziate per essere testate
Test ed ereditarietà Alcune operazioni possono non essere modificate nelle sottoclassi, altre possono essere ridefinite o cancellate: Problema: Come fare il test? Quali operazioni possono essere ri-testate?
1a Soluzione Appiattire l’intera gerarchia e considerare ogni classe come una componente totalmente indipendente: Flattering inheritance Svantaggio: Si perde l’incrementalità e la riusabilità
2a Soluzione Test incrementale Evitare di ripetere il test di elementi che sono ereditati senza cambiamenti, mentre si dovrebbero testare i nuovi elementi e quelli che sono stati ridefiniti Tecnica usata per testare classi appartenenti ad una gerarchia con ereditarietà Quando una classe eredita da una classe base già testate, il test della sottoclasse richiede il test solo di alcuni elementi Una sottoclasse R può essere definita come una classe base P più una modifier M A seconda del tipo di elemento il test può avvenire riusando un caso di test esistente in modo totale o parziale
Test dei costrutti generici Costrutti generici e polimorfismo sono strumenti essenziali per ottenere generalità e riusabilità dei programmi, ma sono sorgente di complessità nel test e nella verifica Genericità: A differenza della programmazione procedurale i moduli generici sono molto presenti e rappresentano la base per la costruzione di librerie e componenti riusabili Problemi nel test: assunzioni sul parametro metodo da usare nel test di un componente generico già riutilizzato
Molte implementazioni per una singola operazione Problemi nel test: Test del polimorfismo Molte implementazioni per una singola operazione La selezione dell’effettivo codice da chiamare è proposta a run-time Problemi nel test: Scelta di come coprire tutte le chiamate alle operazioni polimorfiche Come esercitare tutte le implementazioni di un’operazione Come trattare parametri polimorfici
Soluzioni al problema del test del polimorfismo e costrutti generici Nel caso del polimorfismo, invece, mentre nella programmazione procedurale le chiamate di procedura sono legate staticamente, nella programmazione OO, un’entità può riferire oggetti di classi diverse di una gerarchia, quindi si hanno molte implementazioni per una singola operazione. La selezione dell’effettivo codice da chiamare è proposta a run-time. I problemi che s’incontrano nell’ambito del test sono dovuti alla scelta di come coprire tutte le chiamate alle operazioni polimorfiche, come esercitare tutte le implementazioni di un’operazione o come trattare parametri polimorfici. Il testing strutturale, in questo caso, non può essere trattato statisticamente. Si può dimostrare che tutte le possibili combinazioni di chiamate polimorfiche e parametri polimorfici rischiano di far esplodere in modo combinatorio il numero dei casi di test. Si può ottenere una riduzione di questo effetto attraverso tecniche di analisi statica che permettano d’individuare le effettive chiamate delle unità interessate (data-flow analysis).
Tecniche tradizionali per OO Test di sistema e di accettazione, che si basano su requisiti funzionali gli approcci tradizionali al testing strutturale, che possono essere usati per testare metodi singoli il test di regressione, il quale richiede solo di scrivere nuovi test per le caratteristiche nuove o modificate l’esecuzione di tali test richiede solo modifiche sintattiche
Metriche di prodotto per il software Fattori di qualità ISO 9126 metriche
I fattori di qualità ISO 9126 Opportunità Precisione Interoperabilità Conformità Sicurezza Funzionalità Funzionalità: il grado con il quale il software soddisfa le esigenze stabilite Affidabilità: la quantità di tempo per la quale il software è disponibile all’uso Maturità Resistenza ai guasti Ricuperabilità Affidabilità
I fattori di qualità ISO 9126 Comprensibilità Facilità d’apprendimento Facilità d’uso Facilità d’uso Facilità d’uso: il grado di facilità d’uso del software Facilità di manutenzione: la facilità con la quale è possibile intervenire sul software Facilità d’analisi Facilità di modifica Facilità di collaudo Stabilità Facilità di manutenzione
I fattori di qualità ISO 9126 Comportamento nel tempo Comportamento con le risorse Efficienza Efficienza: il grado con il quale il software utilizza le risorse del sistema in modo ottimale Portabilità: la facilità con la quale il software può essere trasferito da un ambiente ad un altro Adattabilità Installabilità Conformità Sostituibilità Portabilità
Misurazione del software orientato all’obiettivo
[IEEE Standard Glossary Engineering terminology, 1993] Cos’è una metrica? Definizione Misura quantitativa del grado con cui un sistema, un componente o un processo possiede un determinato attributo. [IEEE Standard Glossary Engineering terminology, 1993]
Evidenzia la necessità di: Paradigma GQM Tecnica che identifica metriche significative per ogni parte del processo di sviluppo software. Evidenzia la necessità di: Stabilire un obiettivo di misurazione esplicito, specifico delle attività del processo o delle caratteristiche del prodotto che deve essere valutato Definire un insieme di domande cui occorre rispondere per ottenere l’obiettivo Identificare metriche ben formulate per aiutare a rispondere a queste domande
Metriche calcolate per il prodotto sviluppato Modello di qualità GQM (Goal Question Metric) Stabilire un obiettivo di misurazione esplicito (Goal) Definire un insieme di domande (Question) Identificare metriche ben formulate (Metric) [Basili e Weiss, “ A methodology for collecting valid software engineering data”, IEEE Trans. Software Engineering, vol 10, 1984, pp. 728-738]
Descrizione del modello Assume la seguente forma: Analizzare (nome dell’attività o dell’attributo che deve essere misurato) con lo scopo di (obiettivo generale dell’analisi) con riferimento a (aspetto dell’attività o dell’attributo considerato) dal punto di vista di (persone che hanno un interesse nella misurazione) nel contesto di (ambiente in cui viene svolta la misurazione)
Modello di Progettazione di un GQM Usa i fogli metrici, come strumento operativo per migliorare la leggibilità e tracciare esplicitamente il monitoraggio e il miglioramento continuo GQM/QIP Oggetto dello studio Scopo Prospettiva Punti di Vista Contesto Quality Focus Variation Factors descrive la relazione tra i fattori di variazione e le metriche del quality focus contiene i quesiti e le metriche per la conformità del processo o della caratterizzazione del prodotto Baselines Hypothesis Impact on Baseline Hypothesis contiene i quesiti e le metriche dei modelli di conferma e di validità
Modello di Progettazione di un GQM Usa le tavole di decisione per interpretare i risultati delle misurazioni effettuate e selezionare le attività di miglioramento più adatte. Condizioni (metriche necessarie per l’interpretazione) Stati condizionali: combinazione di valori che possono assumere le condizioni (baseline) Attività di miglioramento Regola di decisione
Metriche di prodotto Metriche per il modello analitico: Funzionalità fornita Dimensioni del sistema Qualità delle specifiche Metriche per il modello progettuale Dell’architettura A livello di componenti Della progettazione dell’interfaccia Specializzate relative al progetto orientato agli oggetti Metriche relative al codice sorgente Di Halstead Di complessità Di collaudo Sulle istruzioni e ramificazioni Relative ai difetti Test dell’efficacia Sul processo
Metriche per il modello concettuale basate sulle funzionalità Metrica dei punti funzione usata per : Stimare il costo o l’impegno necessario per progettare, programmare e collaudare il software Prevedere il numero di errori che verranno rilevati durante il collaudo Prevedere il numero di componenti e/o il numero di righe di codice che comporranno il sistema implementato
Punti funzione Derivati utilizzando una relazione empirica che si basa su misure calcolabili dirette nel dominio delle informazioni del software e valutando la complessità del software Numero di input esterni (EI). Ogni input esterno viene ottenuto da un utente o trasmesso da un’altra applicazione e fornisce dati distinti, orientati all’applicazione, oppure informazioni di controllo. Gli input vengono spesso usati per aggiornare i file logici interni (ILF - Internal Logical File). Numero di output esterni (EO). Ogni output esterno viene creato nell’applicazione e fornisce informazioni all’utente. In questo contesto l’output esterno fa riferimento ai report, agli schemi, ai messaggi d’errore e così via.
Calcolo dei punti funzione Numero di richieste esterne (EQ). Una richiesta esterna viene definita come un input ondine, che produce la generazione di una risposta immediata del software, sottoforma di un output online. Numero di file logici interni (IFL). Ogni file logico interno è un raggruppamento logico di dati che risiedono all’interno dell’applicazione e vengono gestiti tramite gli input esterni. Numero di file dell’interfaccia esterna (EIF). Ogni file dell’interfaccia esterna è un raggruppamento logico di dati che risiede esternamente rispetto all’applicazione, ma che fornisce dati utilizzati dall’applicazione.
Conteggio dei punti funzione Fattore di peso Parametro di misurazione Conteggio Semplice Medio Complesso Input utente 3 4 6 = Output utente 5 7 Interrogazioni utente File 10 15 Interfacce esterne Totale
Conteggio dei punti funzione Dove conteggio-totale è la somma di tutte le voci FP tratte dalla tabella precedente
Valori Fi I valori Fi (i=da 1 a 14) sono fattori di aggiustamento (VAF - Value Adjustment Factors) che si basano sulle risposte alle seguenti domande. Il sistema ha bisogno di operazioni di backup e ripristino di affidabilità? E’ necessario impiegare comunicazioni specializzate per trasferire le informazioni da o verso l’applicazione? Esistono funzioni di elaborazione distribuita? Le prestazioni rappresentano un fattore critico? Il sistema può funzionare in un ambiente operativo esistente, pesantemente utilizzato? Il sistema richiede un inserimento online dei dati? L’inserimento online dei dati richiede che venga realizzata una transazione di input costituita da più schermi o operazioni? I file IFL vengono aggiornati online? Esistono input, output, file o richieste di natura complessa? L’elaborazione interna è complessa? Il codice è progettato per essere riutilizzabile? Nel progetto sono compresi la conversione e l’installazione? Il sistema è progettato per più installazioni in più organizzazioni? L’applicazione è progettata per facilitare le modifiche e la facilità d’uso da parte degli utenti? Occorre rispondere ad ognuna di queste domande utilizzando una scala che va da 0 (non importante o non applicabile) a 5 (assolutamente fondamentale). [Longstreet, “Fundamental of Function Point Analysis”, Longstreet Consulting, Inc. 2002]
[Whitmire, “Object-Oriented Design Measurement”, Wiley, 1997] Metriche per il modello progettuale Metriche per la progettazione orientata agli oggetti Metriche CK Metriche Mood Secondo Whitmire, esistono nove caratteristiche distinte e misurabili di un progetto orientato agli oggetti. [Whitmire, “Object-Oriented Design Measurement”, Wiley, 1997]
Caratteristiche di un progetto orientato agli oggetti 1/2 Dimensioni. Le dimensioni sono definite in termine di quattro elementi: numerosità, volume, lunghezza e funzionalità. La numerosità viene misurata conteggiando staticamente le entità orientate agli oggetti (es. classi o operazioni). Le misure del volume sono identiche a quelle di numerosità, ma vengono raccolte in modo dinamico. La lunghezza misura una catena di elementi progettuali interconnessi (es. profondità di un albero di ereditarietà). Le metriche di funzionalità forniscono un’indicazione indiretta del valore fornito al cliente da un’applicazione orientata agli oggetti. Complessità. E’ vista in termini di caratteristiche strutturali, esaminando le relazioni tra le classi di un progetto orientato agli oggetti. Accoppiamento. Le connessioni fisiche tra gli elementi del progetto orientato agli oggetti rappresentano l’accoppiamento all’interno del sistema.
Caratteristiche di un progetto orientato agli oggetti 2/2 Sufficienza. E’ il grado in cui un’astrazione possiede le caratteristiche richieste o il grado in cui un componente progettuale possiede le caratteristiche della sua astrazione, dal punto di vista dell’applicazione corrente. Completezza. E’ l’insieme di caratteristiche rispetto alle quali si deve confrontare l’astrazione o il componente del progetto. Coesione. Si verifica quando un componente ad oggetti viene progettato in modo che tutte le operazioni collaborino per un unico obiettivo ben definito. Primitività. Indica il grado di atomicità di un’operazione. Similarità. E’ il grado in cui due o più classi sono simili in termini di struttura, funzione, comportamento o scopo. Volatilità. Misura la probabilità che si verifichi una modifica.
Metriche orientate alle classi: metriche CK Una delle metriche più utilizzate per il software orientato agli oggetti è quella proposta da Chidamber e Kemerer da cui il nome di metriche CK. Gli autori hanno proposto sei metriche di progettazione basate su classi per un sistema orientato agli oggetti. [Chidamber, Kemerer, “A metrics Suite for Object-Oriented Design”, IEEE Trans. Software Engineering, vol. SE – 20, no 6, June 1994, pp. 476-493.]
1. Metodi pesati per classe Metodi pesati per classe (WMC - Weighted Methods per Class). Una classe definisce n metodi di complessità c1, c2, …, cn WMC = Σ ci i=1,...,n La metrica di complessità prescelta può essere qualsiasi. Se la complessità di ciascun metodo assume il valore 1, WMC è pari al numero dei metodi della classe. Se si usa la complessità di McCabe: c = classe di cui si sta valutando WMC VG(m) = complessità ciclomatica del metodo m MIm(c) = insieme dei metodi implementati nella classe c Il numero di metodi e la loro complessità sono indice della complessità della classe. Infatti, si preferisce un valore di WMC basso, perché maggiore è il numero dei metodi, più complesso è l’albero d’ereditarietà; nonché, all’aumentare di questo valore, è probabile che la classe divenga più specifica dell’applicazione, limitando il suo potenziale riutilizzo.
2. Profondità dell’albero di ereditarietà Profondità dell’albero di ereditarietà (DIT - Depth of the Inheritance Tree). Indica la distanza massima di un nodo (una classe) dalla radice dell’albero rappresentante la struttura ereditaria. Maggiore è la profondità della classe, nella gerarchia, maggiore è il numero di metodi che essa può ereditare, rendendo più complesso predire il suo comportamento. Inoltre, alberi di ereditarietà con maggiore profondità possono aumentare la complessità del progetto (più classi e metodi sono coinvolti). Infine, maggiore è la profondità di una classe, in una gerarchia, maggiore è il riuso potenziale dei metodi ereditati.
3. Numero di figli Numero di figli (NOC - Number Of Children). Indica il numero di sottoclassi, figlie di una superclasse. Al crescere del NOC, cresce il livello di riuso, ma tende ad ‘evaporare’ l’astrazione della classe madre. Inoltre, aumenta la possibilità che una classe figlia non sia membro appropriato della madre. Al crescere del NOC, cresce la quantità di casi di test necessari a testare ogni figlia.
4. Risposte per classe Risposte per classe (RFC - Response For a Class). Indica l’insieme dei metodi che possono essere eseguiti in risposta ad un messaggio ricevuto da un oggetto della classe. A valori elevati di RFC, cresce lo sforzo per il testing ed aumenta anche la complessità progettuale della classe.
5. Accoppiamento fra le classi di oggetti Accoppiamento fra le classi di oggetti (CBO - Coupling Between Object classes). Registra il numero di collaborazioni di una classe, ovvero il numero di altre classi cui essa è accoppiata. Un eccessivo accoppiamento è negativo per la modularità ed il riuso; mentre più una classe è indipendente più è facilmente riusabile. Per migliorare la modularità, l’accoppiamento va tenuto al minimo; esso influisce sull’impatto di modifiche in altri moduli.
6. Mancanza di coesione nei metodi Mancanza di coesione nei metodi (LCOM - Lack of Cohesion in Methods). Indica la coesione tra gli elementi di una classe ed è espressa tramite il numero di metodi che accedono agli stessi attributi di una classe. In questo caso, se il suo valore è alto, i metodi possono essere accoppiati l’uno all’altro tramite gli attributi, ma questo comporta una maggiore complessità del progetto della classe. Si preferisce LCOM basso e coesione alta.
Metriche orientate alla classe: le metriche MOOD Harrison, Counsell e Nithi propongono un insieme di metriche per la progettazione orientata agli oggetti che rappresentano indicatori quantitativi delle caratteristiche di progettazione orientata agli oggetti. [Harrison, Consell e Nithi, “An evaluation of the MOOD Set of Object-Oriented Software Metrics”, IEEE Trans. Software Engineering, vol. SE – 24, no. 6, June 1998, pp. 491-496.]
Fattore di ereditarietà dei metodi Fattore di ereditarietà dei metodi (MIF- Method Inheritance Factor) Il grado in cui l’architettura della classe di un sistema orientato agli oggetti utilizza l’ereditarietà sia nei metodi che negli attributi è definita nel seguente modo: dove la sommatoria si verifica per i=1…Tc; Tc è definito come il numero totale di classi dell’architettura; Ci è una classe all’interno dell’architettura = il numero di metodi che possono essere richiamati in associazione con Ci; = il numero di metodi dichiarati in Ci =il numero di metodi ereditati (ma non modificati) in Ci. Il MIF definisce un’indicazione dell’impatto dell’ereditarietà sul software.
Fattore di accoppiamento Fattore di accoppiamento (CF – Coupling Factor) dove la sommatoria si verifica per i=1…Tc e j=1… Tc; la funzione is_client=1 se e solo se esiste una relazione fra la classe client Cc e la classe server Cs e se Cc ≠ Cs =0 altrove. Per quanto riguarda il CF, se aumenta, aumenta anche la complessità del software e questo può peggiorare la comprensibilità e la facilità di manutenzione oltre che le possibilità di riutilizzo.
Metriche orientate agli oggetti di Lorenz e Kidd Lorenz e Kidd dividono le metriche basate su classi in quattro grandi categorie: dimensioni, ereditarietà, aspetti interni e aspetti esterni. Le metriche orientate alle dimensioni per una classe orientata agli oggetti considerano il numero di attributi ed operazioni per una singola classe e calcolano la media per l’intero sistema orientato agli oggetti. Le metriche basate sull’ereditarietà considerano il modo in cui le operazioni vengono riutilizzate nella gerarchia di classi. Le metriche per gli aspetti interni della classe considerano problemi quali la coesione delle caratteristiche del codice. Infine, le metriche sugli aspetti esterni della classe considerano i fattori di accoppiamento e riutilizzo. Un campione delle suddette metriche è rappresentato dalle due seguenti. [Lorenz, Kidd, “Object-Oriented Software Metrics”, Prentice-Hall, 1994 ]
Dimensioni della classe Dimensioni della classe (CS – Class Size). Le dimensioni generali di una classe possono essere determinate come o il numero totale di metodi (privati ed ereditati) o come il numero totale di attributi (privati ed ereditati). In questo caso, se il CS è troppo alto, indica una classe con troppe responsabilità e ciò riduce la riutilizzabilità della classe e ne complica l’implementazione ed il collaudo.
Numero di operazioni aggiunte da una sottoclasse Numero di operazioni aggiunte da una sottoclasse (NOA – Number of Operations Added). Le sottoclassi vengono specializzate aggiungendo operazioni ed attributi. Il NOA, se aumenta, comporta una deviazione della sottoclasse rispetto all’astrazione determinata dalla superclasse. In generale, a mano a mano che si approfondisce la gerarchia di classi (DIT più alto), il NOA ai livelli più bassi della gerarchia dovrebbe calare.
Metriche per il codice sorgente Halstead ha definito alcune leggi quantitative per lo sviluppo di software, derivate dopo la generazione del codice. Le misure di Halstead si basano su quattro numeri ricavabili direttamente dal codice sorgente: n1 = il numero di operatori distinti n2 = il numero di operandi distinti N1 = il numero totale di operatori N2 = il numero totale di operandi e sono descritte dalle seguenti metriche: Lunghezza del programma N= N1+ N2 Vocabolario del programma n= n1 + n2 Volume V= N * (LOG2 n) Difficoltà D= (n1 /2) * (N2 / n2) Sforzo E= D * V [Halstead, “Element of Software Science”, North-Holland, 1977.]
Metriche di complessità Per misurare la complessità del flusso di controllo di un programma si dispone di numerose metriche, molte delle quali basate su una rappresentazione detta grafo di flusso. McCabe e Watson indicano alcuni impieghi importanti delle metriche di complessità. La più diffusa fra le metriche di complessità è la complessità ciclomatica. Essa è stata definita da Thomas McCabe nel 1976 e rappresenta il numero di percorsi linearmente indipendenti in un modulo. E’ calcolata sulla base del grafo di flusso di un programma/modulo software. I nodi (n) di tale grafo rappresentano le linee di codice, mentre gli archi, il flusso di controllo [McCabe, Watson, “Software Complexity”, Crosstalk, vol.7, no. 12, December 1994, pp 5-9.]
Grafo di flusso per un generico programma La complessità ciclomatica è calcolata come V(G)=e-n+2. Essa è legata alla frequenza degli errori nel sistema. Di solito, se ne preferisce un valore basso al fine di garantire una buona manutenibilità e testabilità.
Esempio applicativo del modello GQM
Valutazione di metriche secondo il modello GQM Fattori di qualità ISO 9126 considerati: facilità d’uso facilità di manutenzione portabilità PRODOTTO SOFTWARE SVILUPPATO Facilità d'uso Facilità di manutenzione Portabilità GOAL 1 X GOAL 2 GOAL 3
Quanto è profonda la gerarchia di classi? GOAL 1 - Facilità d’uso G1- Facilità d’uso Quanto è profonda la gerarchia di classi? Qual è il grado di coesione interna di una classe? Quanto è complessa una classe? QF1- Comprensibilità QF2-Facilità d’apprendimento QF3- Facilità d’uso Q1 Q2 Q3 Q4 Q5 Qual è il grado di complessità della risposta ad un messaggio? Quanto è generica una classe? DIT RFC LCOM CS NOA WMC MIF Vg
Prodotto software sviluppato Foglio metrico Oggetto dello studio Scopo Prospettiva Punti di vista Contesto Prodotto software sviluppato Valutazione Facilità d'uso Sviluppatore Prodotto sviluppato Quality Factors Variation Factors DITe: profondità dell’albero di ereditarietà NOAe: numero di operazioni aggiunte ad una sottoclasse MIFe: fattore di ereditarietà dei metodi RFCe: risposte per classe LCOMe: mancanza di coesione nei metodi CSe: dimensione della classe WMCe: metodi pesati per classe Vge: complessità ciclomatica Coesione interna Profondità della gerarchia di classe Complessità di classe Generalizzazione di classe Complessità di risposta Foglio metrico:descrivono un GQM Quality Focus: rappresentano i quesiti e le metriche del modello primario Variation Factors: contiene i quesiti e le metriche per la conformità del processo o della caratterizzazione del prodotto
Prodotto software sviluppato Oggetto dello studio Scopo Prospettiva Punti di vista Contesto Prodotto software sviluppato Valutazione Facilità d'uso Sviluppatore Prodotto sviluppato Baseline Hypothesis Baseline Impacts DITa<30% NOAa<30% MIFa<30% RFCa<20% LCOMa>80% CSa<20% WMCa<30% Vga<20% La profondità gerarchica di classe influenza la profondità dell’albero di ereditarietà, il numero di azioni introdotte nelle sottoclassi ed il fattore di ereditarietà dei metodi attesi rispetto a quelli effettivi. La complessità di classe influenza il valore atteso della complessità ciclomatica e della complessità dei metodi pesati per classe rispetto ai valori effettivi. La generalizzazione influenza il valore atteso di operazioni aggiunte alle sottoclassi e di dimensioni della classe rispetto a quello effettivo. La coesione influenza il valore atteso rispetto a quello effettivo. Baseline Hypothesis: rappresenta i quesiti e le metriche dei modelli di conferma e di validità Baseline Impact: descrive la relazione tra i fattori di variazione e le metriche del quality focus
Tavola di decisione per Goal 1 Legenda <=30% La % si riferisce al valore atteso >30% <=20% >20% >=80% <80% c1. DITa<DITe Y c2. NOAa<NOAe c3. MIFa<MIFe c4. RFCa<RFCe c5. LCOMa>LCOMe N c6. CSa<CSe c7. WMCa<WMCe c8. Vga<Vge a1. goal soddisfatto x a2. aumentare coesione a3. ridurre la profondità della gerarchia di classi a4. ridurre la complessità di classe a5. aumentare la generalizzazione 1 2 3 4 5 6 7 8 9 10 11 12 13 14
GOAL 2 - Facilità di manutenzione G2- Facilità di manutenzione Qual è la capacità di funzionamento delle classi figlie in una gerarchia di classe? Qual è il grado di profondità di una gerarchia di classe? QF4- Facilità di analisi QF5- Facilità di modifica QF6- Facilità di collaudo Q6 Q7 Q8 Q9 Qual è il grado di accoppiamento tra le classi? Qual è il grado d’impegno per il collaudo? NOC CBO WMC RFC MIF CF
Prodotto software sviluppato Facilità di manutenzione Foglio metrico Oggetto dello studio Scopo Prospettiva Punti di vista Contesto Prodotto software sviluppato Valutazione Facilità di manutenzione Sviluppatore Prodotto sviluppato Quality Factors Variation Factors NOCe: numero di classi figlie CBOe: accoppiamento fra le classi di oggetti CFe: fattore di accoppiamento MIFe: fattore di ereditarietà dei metodi RFCe: risposte per classe WMCe: metodi pesati per classe Funzionamento classi figlie Accoppiamento Profondità della gerarchia di classe Impegno del collaudo
Prodotto software sviluppato Facilità di manutenzione Oggetto dello studio Scopo Prospettiva Punti di vista Contesto Prodotto software sviluppato Valutazione Facilità di manutenzione Sviluppatore Prodotto sviluppato Baseline Hypothesis Baseline Impacts NOCa<30% CFa<20% CBOa<20% MIFa<30% RFCa<20% WMCa<30% Il funzionamento delle classi figlie influenza il valore atteso del numero di classi figlie rispetto a quello effettivo. L’accoppiamento influenza il numero atteso di collaborazioni con le classi rispetto ai valori effettivi. La profondità gerarchica di classe influenza il fattore di ereditarietà dei metodi attesi rispetto a quelli effettivi. L’impegno del collaudo influenza il numero atteso di risposte per classe ed il numero atteso di metodi per classe rispetto ai valori effettivi.
Tavola di decisione per Goal 2 Legenda <=30% La % si riferisce al valore atteso >30% <=20% >20% c1. NOCa<NOCe Y c2. CBOa<CBOe N c3. CFa<CFe c4. MIFa<MIFe c5. RFCa<RFCe c6. WMCa<WMCe a1. goal soddisfatto x a2. riduzione classi figlie a3. riduzione accoppiamento a4. riduzione profondità gerarchica a5. riduzione impegno collaudo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Qual è il grado di profondità di una gerarchia di classe? GOAL 3 - Portabilità G3- Portabilità Qual è il grado di profondità di una gerarchia di classe? QF7- Adattabilità QF8- Riutilizzo QF9- Sostituibilità Q10 Q11 Q12 Qual è il grado di specificità della classe? Qual è il grado di accoppiamento tra le classi? NOC CBO WMC DIT CF
Prodotto software sviluppato Foglio metrico Oggetto dello studio Scopo Prospettiva Punti di vista Contesto Prodotto software sviluppato Valutazione Portabilità Sviluppatore Prodotto sviluppato Quality Factors Variation Factors DITe: profondità dell’albero di ereditarietà NOCe: numero di classi figlie CBOe: accoppiamento fra le classi di oggetti CFe: fattore di accoppiamento WMCe: metodi pesati per classe Specificità dell’applicazione Accoppiamento Profondità della gerarchia di classe
Prodotto software sviluppato Oggetto dello studio Scopo Prospettiva Punti di vista Contesto Prodotto software sviluppato Valutazione Portabilità Sviluppatore Prodotto sviluppato Baseline Hypothesis Baseline Impacts DITa>=70% NOCa>=70% CBOa<20% CFa<20% WMCa<30% La specificità dell’applicazione influenza il numero atteso di metodi rispetto a quello effettivo. L’accoppiamento influenza il valore atteso di collaborazioni tra le classi rispetto al valore effettivo. La gerarchia di classi influenza il valore atteso della profondità dell’albero di ereditarietà rispetto a quello effettivo.
Tavola di decisione per Goal 3 Legenda: <=30% La % si riferisce al valore atteso >30% <=20% >20% >=70% <70% c1. DITa>DITe Y c2. NOCa>NOCe N c3. CBOa<CBOe c4. CFa<CFe c5. WMCa<WMCe a1. goal soddisfatto x a2. aumento profondità gerarchica a3. riduzione specificità di classe a4. riduzione accoppiamento 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Conclusioni Metriche calcolate Prospettive future WMC basso Maggiori possibilità di riutilizzo delle classi DIT basso Minore complessità progettuale Possibilità di prevedere il comportamento delle classi NOC nullo Sistema non necessita di numerosi collaudi RFC basso Semplicità di collaudo CBO basso Riutilizzo possibile delle classi Semplificazione delle modifiche e del collaudo che verifica tali modifiche
Conclusioni Metriche calcolate Prospettive future LCOM basso Minore complessità progettuale MIF basso Ereditarietà ininfluente sul software CF basso Minore complessità software Maggiore comprensibilità Facilità di manutenzione Possibilità di riutilizzo CS basso Classe con poche responsabilità Semplicità d’implementazione e collaudo Vg basso Buona manutenibilità e testabilità