1 il testing a. fantechi area di ricerca di trieste 19-20/4/2001
TRANSCRIPT
1
Il Testing
A. Fantechi
Area di Ricerca di Trieste
19-20/4/2001
2
Attivita` di Verifica sul codice
• Verifica che il codice soddisfi le sue specifiche• Testing = Attivita` mirante alla scoperta di
eventuali malfunzionamenti del prodotto software dovuti a “guasti” (anomalie – difetti – faults – bugs - bachi) tramite l’esecuzione del codice stesso, fornendogli opportuni dati in ingresso (analisi dinamica)
• Debugging: attivita` di localizzazione e rimozione dei guasti.
3
Teoria del testingProgramma P P: D --> R
ok(P,d) sse P sul dato di ingresso d X D da` il risultato aspettato
ok(P) sse per ogni d X D ok(P,d) (P è corretto)
Test T = sottoinsieme di D. (test suite)
t dato di test se tX T
P è corretto per un test T sse per ogni tX T ok(P,t)
4
Scopo dell'attività di testing è la rilevazione di malfunzionamenti, quindi:
un test T ha successo per un programma T se rileva uno o più malfunzionamenti in P,
successo(T,P) = not ok(P,T)
un test che non rileva malfunzionamenti manifesta non la correttezza del programma, ma la sua inadeguatezza *
un test T per P e' detto ideale se l'insuccesso di T implica la correttezza di P OK(P,T) => ok(P)
5
* Dipende da quale si considera sia lo scopo dell’attivita` di testing: questa definizione va bene se vediamo il testing come attivita` rivolta alla caccia di guasti (bug finding)
Il testing puo` essere visto anche come attivita` mirata ad aumentare la confidenza nella bonta` del prodotto assenza di guasti
Testing come attivita` svolta da un “ente” indipendente da quello di sviluppo.
6
Criteri di selezione di test (per approssimare test ideali)
Un criterio di selezione C per un programma P è un insieme di predicati sul domino di ingresso D
Un test T è selezionato da C se per ogni t X T esiste cX C tale che c(t) è vero e per ogni c X C esiste t X T tale che c(t) è vero
Esempio: C= {(esiste t X T: t<0),(0 X T),(esiste t XT: t>0) }
test selezionati da C: {-4,0,5}{-2,9,0,8,-12,6}
Criterio di test: C = {( t X D => t X T)} test esaustivo
7
Un criterio di selezione C è affidabile per P se, per ogni coppia di test T1, T2 selezionati da C, successo(T1,P) <=> successo(T2,P)
Un criterio di selezione C è valido per P se, qualora P non sia corretto, esiste almeno un test T selezionato da C: successo(T,P)
Teorema di Goodenough e Gerhart
affidabile(C,P) and valido(C,P) and selezionato(C,T) and not successo(T,P)
=> ok(P)
8
Teorema di Howden:Non esiste un algoritmo che, dato P qualsiasi, generi un test ideale finito (test definito da un criterio affidabile e valido)
Tesi di DijkstraIl test di un programma può rilevare la presenza di malfunzionamenti, ma non dimostrarne l'assenza
Teorema di WeyukerDato P qualsiasi, i seguenti problemi sono indecidbili:• esiste almeno un dato di ingresso che causa l'esecuzione di un particolare comando?• esiste un particolare dato di ingresso che causa l'esecuzione di una particolare condizione (branch)?• è possibile trovare almeno un dato di ingresso che causa l'esecuzione di ogni comando di P?• è possibile trovare almeno un dato di ingresso che causa l'esecuzione di ogni condizione (branch) di P?• è possibile trovare almeno un dato di ingresso che causa l'esecuzione di ogni cammino di P?
9
LIVELLI DI TEST
•test di unità - mirato alla correttezza degli algoritmi.
•test di integrazione - mirato alla correttezza delle interfacce.
•test di sistema - affidabilità, sicurezza e prestazioni.
•test di accettazione - imposto dal cliente, verifica che il programma fa quello che voleva.
•test di regressione - verifica che non si siano introdotti errori in versioni successive
10
Test di Unità
• Test funzionale (black box) • Test strutturale (white box)
• Test statistico
Variano per il principio su cui si basa il criterio di selezione dei test.
11
Test funzionale (black box)
Il criterio di selezione si basa sulle specifiche funzionali dell’unita sotto test, ad esempio attraverso:
EQUIVALENCE PARTITIONINGSi divide il dominio di input del programma in classi con l'ipotesi che un caso di test per ciascuna classe sia rappresentativo di tutti i valori della classe. Per ciascuna condizione di input si associano almeno due classi di equivalenza, una valida ed una invalida.
BOUNDARY VALUE ANALYSISSi scelgono i casi di test in prossimità della frontiera delle classi.
12
Es. Supponiamo di avere una unita che si puo` comportare differentemente per valori negativi o positivi dei dati in ingresso, ma il valore effettivo non e` particolarmente interessante. Un criterio di test basato sul partizionamento puo’ essere: C= {(esiste t X T: t<0)(esiste t XT: t>=0) }Cioe` si testa l’unita` con almeno un valore negativo e uno positivo.
Se vogliamo prestare attenzione ai valori di forntiera (boundary analysis), possiamo includere lo zero:
C= {(esiste t X T: t<0),(0 X T),(esiste t XT: t>0) }
13
Altre tecniche…
CAUSE-EFFECT GRAPHINGSi disegna un grafo dove gli archi rappresentano operatori logici le specifiche vanno ridotte a condizioni booleane, rappresentanti fatti di ingresso o uscita (“stanza doppia”, “alta stagione”, …).Ogni condizione booleana in ingresso (uscita) identifica un sottoinsieme dei dati in ingresso (in uscita).
ERROR GUESSINGTecnica ad-hoc basata sull' intuito e sull' esperienza di chi esegue il test.
14
Testing basato sulla specifica
specifica formalePossono essere identificate piu` facilmente le partizionie i boundary cases.
puo` essere automatizzatoEs. Generazione di Test case da Finite state automata (UML state diagrams)
15
Test strutturale (white box)Il criterio di selezione dei test si deriva dalla struttura stessa del programma
(in particolare, dal grafo di flusso)
Si selezionano quei test che esercitano tutte le strutture del programma
Occorre decidere quali sono le strutture di riferimento: vari tipi di testing a seconda della struttura scelta
Occore una misura che ci permette di decidere se sono state esercitate tutte le strutture
misura di copertura (coverage)
16
Statement testing• I test sono selezionati in base alla loro capacita` di coprire i comandi
(nodi del grafo di flusso)
• Coverage = numero comandi eseguiti / numero totale comandi
Un test e` selezionato se garantisce una copertura dei comandi uguale a 1
In questo modo si garantisce che I comandi sono tutti eseguiti almeno una volta.
Non si garantisce di aver percorso tutte le “strade” (rami) almeno una volta:
if (c) { comando1 }
comando2
Un test che rende la condizione c vera esegue tutti i comandi, ma non tutti i rami
17
Branch testing
• Si richiede che in questo caso vengano percorsi dai test tutti i rami del programma almeno una volta. (ogni arco del grafo di flusso deve appartenere almeno ad un cammino di esecuzione esercitato dal test).
• Coverage = numero rami percorsi / numero totale rami
Un test e` selezionato se garantisce una copertura dei rami uguale a 1
La copertura dei rami implica la copertura dei cammini
18
Condition testingif (x>1 && y==0) {comando1}else{comando2}
In questo caso sia la copertura dei comandi che quella dei rami non garantiscono che si siano testate tutte le combinazioni delle due condizioni in and.
Ad esempio, un test potrebbe esercitare solamente le situazioni: {x = 2, y =0} e { x=2, y=1}, garantendo la piena coperura dei rami e quindi dei comandi
Cfr. con programma equivalente:if (x>1) if (y==0) {comando1} else {comando2}else {comando2}In cui lo stesso test da` una copertura rami e comandi del 60%
19
• Basic condition coverage =
numero totale dei valori di verita` assunti da tutte le condizioni basiche numero totale dei valori di verita` delle condizioni basiche
dove per numero totale dei valori di verita` delle condizioni basiche si intende 2 volte il numero delle condizioni basiche, considerando cioe` ogni condizione valutata sia a true che a false, ma senza considerare tute le possibili combinazioni dei due valori di verita` su tutte le condizioni basiche.
Nel caso precedente, I due casi di test {x = 0, y =0} e { x=2, y=1}, peremettono di avere una copertura del 100% delle condizioni basiche.
Come e` evidente in questo caso, la piena copertura delle condizioni basiche non garantisce la copertura dei rami.
20
•Vengono percio` utilizzati altri criteri, che considerano non solo i valori di verita` delle condizioni basiche, ma anche la decisione finale MCDC Modified Condition / Decision Coverage
MCDC implica copertura dei rami
esempio
D’altra parte, se si considerano tutte le possibili combinazioni dei valori di verita` di tutte le condizioni basiche (compound condition coverage), le possibilita` da coprire sono in numero di 2 elevato al numero di condizioni basiche, equindi diventano un numero eccessivo per condizioni appena complesse.
21
if (pos < parseArray.length && (parseArray[pos] == ‘{‘ || parseArray[pos] == ‘}‘ || parseArray[pos] == ‘|‘)) {continue;}
22
if (pos < parseArray.length && (parseArray[pos] == ‘{‘ || parseArray[pos] == ‘}‘ || parseArray[pos] == ‘|‘)) {continue;}
roomopen
bar
close
Test case room open close bar result
1 false false
2 true true true
3 true false true true
4 true false false true true
5 true false false false false
23
Path testing
• I criteri di copertura finora visti non sempre consentono di rilevare quei guasti che vengono attivati solo da particolari cammini di esecuzione nel programma.
• Copertura dei cammini:
Cpath =
numero dei cammini eseguiti / numero totale dei cammini
24
C1
C2
S1 S2
S3 S4
0 1
0 1
test C1 C2 cammino
T1 0 0 S1 S3
T2 0 1 S1 S4
T3 1 0 S2 S3
T4 1 1 S2 S4
25
• Sono sufficienti i due casi di test T1 e T4 per ottenere una copertura del 100% dei rami. Alternativamente, sono sufficienti i due casi di test T2 e T3.
• Per una copertura del 100% dei cammini sarebbero necessari tutti i casi di test T1, T2, T3, T4.
• Il caso precedente è il solo caso, in assenza di cicli, in cui la copertura dei cammini e la copertura dei rami differiscono; nel caso in cui ho due if then else annidati, infatti, sia i rami che i cammini sono 3.
• Si noti che il numero dei cammini e’ esponenziale con il numero degli if.
26
Il numero dei cammini e` pero`in generale illimitato, per la presenza di cicli.
• Per questo motivo la path coverage non viene spesso nemmeno presa in considerazione in quanto tale. Semmai, quando si parla di copertura di cammini in presenza di cicli si pone in genere un limite superiore al numero di iterazioni che vengono svolte. Seguendo la terminologia introdotta da Miller, si parla di copertura Ct k, dove k è il numero di possibili diverse iterazioni che si vogliono considerare. Ad esempio, k=2 significa che si vuole eseguire il ciclo con 0, 1, 2 iterazioni.
• Un modo comune di considerare la copertura dei cammini è quello di utilizzare k=1. Se si adotta quindi la copertura Ct(k=1) per ogni ciclo si considererà sia il cammino che non esegue affatto le azioni interne al ciclo sia quello che le segue (almeno) una volta. In questo modo si ottiene comunque la copertura dei rami. Anche qui occorre fare attenzione che si parla di "almeno" perchè non sempre sarà possibile individuare facilmente il caso di test che fa eseguire esattamente una volta le azioni del ciclo; inoltre, il caso del for con numero di iterazioni fissato staticamente sarà sempre eseguito esattamente quel numero di volte.
• Si parla anche di criterio di N-copertura: copertura legata al numero di iterazioni del loop.
27
Cyclomatic testing
• Si noti comunque che il numero dei cammini, anche limitando il numero delle iterazioni, rimane esponenziale con il numero if indipendenti tra loro.
• Sono stati definiti vari criteri di copertura basati sui cammini, tra cui il testing ciclomatico, che si basa sul numero di Mc Cabe: si conta il numero di cammini linearmente indipendenti testati, e si considera soddisfatto il criterio quando il numero dei cammini testati raggiunge il numero ciclomatico della unita` sotto test.
28
Copertura (coverage)
• Il concetto di copertura può essere utilizzato come metrica per valutare l'effettività dei test effettuati: ad esempio, i casi di test T1 e T4 forniscono il 100% di copertura dei rami e il 50% di copertura dei cammini.
• Un criterio quantitativo di copertura viene spesso utilizzato come soglia, (es. 90% dei cammini) raggiunta la quale l'attività di testing viene considerata troppo costosa e viene quindi interrotta.
• Si puo` usare una misura di copertura strutturale per valutare l’efficacia di una fase preliminare di testing funzionale e per capire se questa ha lasciato scoperte (non esercitandole) alcune strutture del programma
29
Unfeasible paths
• Ritornando all'esempio di cui sopra, si noti però che potrebbe accadere che le condizioni C1 e C2 siano dipendenti tra loro: si consideri ad esempio il caso più banale, in cui C1=C2. In questo caso si potranno al massimo esercitare due cammini, S1-S3 e S2-S4. Non si potrà cioè ottenere la copertura del 100% dei cammini, ma solo il 50 %. Questo se si considerano i cammini sul grafo, ma i cammini possibili del programma (feasible paths o achievable paths) sono coperti al 100%.
• La possibile presenza di cammini unfeasible (o Infeasible) rende quindi più difficoltosa la misurazione della copertura di cammini; tale presenza in generale non può essere rilevata da un'analisi statica, ed anche la completa rilevazione dei cammini unfeasible attraverso un'analisi dinamica è stata dimostrata essere teoricamente impossibile
30
Unfeasible paths
• In alcuni casi, la presenza di cammini Unfeasible e` voluta: Defensive Programming: si testano condizioni normalmente vere,
che possono risultare false solo in presenza di guasti hardware/software: nel caso risultino false si effettuano percio`opportune azioni di trattamento del guasto.
• In altri casi, la presenza di cammini Unfeasible e` indice di disattenzione nella stesura (o nel riutilizzo)del codice.
• A fronte di percentuali di copertura minori del 100%, si
dovra` giustificarne il motivo (normative DO-178B): si potra` cosi` duistinguere i casi di Defensive Programming da quelli di codice non sufficientemente curato.
31
Data flow testing
• Nella più recente produzione scientifica sul testing, non si tenta di ottenere la copertura dei cammini, ma semmai qualcosa di intermedio tra la copertura dei rami e quella dei cammini. Viene infatti definita una "data-flow" coverage, in cui i casi di test sono selezionati sulla base dell'utilizzo dei dati: ad esempio un cammino dove la stessa variabile viene prima scritta e poi letta: si considerano solo i cammini da assegnamenti a variabili a usi successivi della variabili: i cammini analizzati hanno dunque una rilevanza diretta sul modo con cui i programmi trattano i dati.
32
Ordinamento dei criteri di copertura
Cyclomatic t. MCDC testing
Basic cond. testing
Branch testing
Statement testing
N-Path testing
Path testing
Compound cond. testing
33
Ordinamento dei criteri di copertura
Cyclomatic t. MCDC testing
Basic cond. testing
Branch testing
Statement testing
N-Path testing
Path testing
Compound cond. testing
TEORICI
34
Funzionale vs. strutturale
Il testing funzionale e quello strutturale sono complementari:
• Una specifica di una tabella lascia all’implementatore la scelta della struttura dati da utilizzare. Se la scelta e` per una tabella hash, vi saranno diverse porzioni di codice eseguite a seconda che vi siano o meno collisioni. Test derivati dalla specifica non possono garantire di coprire sia il caso di collisione che quello di non collisione, mentre il piu’ semplice criterio di copertura strutturale lo garantisce.
• Viceversa, il caso dei missing paths. Questi guasti derivano dal non aver considerato alcuni aspetti dalla specifca, e possono essere rivelati solo da un testing basato sulla specifica, e non da un testing strutturale.
35
Test statistico
Mentre nei test funzionale e strutturale i dati di test sono forniti da un criterio deterministico, nel test statistico sono casuali.
In realtà posso usarlo in combinazione con i precedenti ed ottenere 4 tipi di test:
funzionale deterministico, funzionale statistico, *strutturale deterministico,strutturale statistico. *
(* il criterio di selezione dei test e` deterministico e definisce degli intervalli; i dati di test possono essere scelti casualmente in tali intervalli)
36
Il test di una unita` richiede la costruzione di:
• Stub Modulo fittizio che presenta la stessa interfaccia di un modulo invocato dall’unita`
sotto test.• Drivers
Modulo che ha la funzione di invocare l’ unita` sotto test passandole I casi di test individuati
• Oracolo Puo’ essere un programma, ma anche l’utente umano, e rappresenta l’entita` che
decide se il test e’ passato o fallito.
• Automazione del testing: strumenti di supporto alle attivita’ di costruzione di Drivers, Stubs, e Oracoli
Cantata, Adatest, Logiscope,…..
37
Test di integrazione
• In seguito al test dei singoli moduli, si esegue il test di integrazione per verificare la correttezza del programma complessivo, e l’assenza di anomalie sulle interfacce tra i moduli. Per questo si seguono due metodi:
approccio non incrementale
approccio incrementale
L’approccio non incrementale prevede di assemblare tutti i moduli e realizzare immediatamente l’analisi globale del sistema. È detto anche big bang test. Suppone di aver completato il test di unita` per ogni modulo.
38
Test di integrazione
•Nell’approccio incrementale si parte testando dei singoli moduli, collegandoli poi con i moduli chiamanti o chiamati, e testando il sottosistema ottenuto, e cosi’ via fino a costituire il sistema complessivo.
•Non richiede di aver svolto il test di unita` di tutti i moduli
•Esercita piu’ a lungo ogni singolo modulo
•Permette di localizzare piu` facilmente le anomalie di interfaccia tra moduli.
39
Test di integrazione - STRATEGIE DI TEST
TopDown Bottom Up
Risultati veloci inizia a sistema ultimatostub complessi (emulatori, tabelle) driver semplicioutput artificiale per simulare parallelismo i moduli sottostanti
40
Test di integrazione - Criteri di copertura
Criteri di copertura sono stati definiti anche per l’attivita` di test di integrazione. In particolare, il criterio di copertura delle chiamate di procedura ci dice se il testing di integrazione ha esercitato tutte le procedure.
Con una granularita` piu` fine, il criterio di copertura dei punti di ingresso e dei punti di uscita dalle procedure consente di sapere se sono stati esercitati tutti i punti di ingresso e i punti di ritorno da una procedura o funzione.
41
Test di sistema
• Con il test di integrazione si è già realizzato il test delle funzionalità dell’intero sistema, ora si vogliono analizzare alcune proprietà globali che non hanno senso se viste nell’ambito del singolo modulo. Esempi:
• - Test di stress ( overload ) : si vuole verificare non solo che il programma funzioni secondo le specifiche, ma anche che si comporti in modo corretto in condizioni di carico di lavoro eccezionale. Per esempio: un sistema per basi di dati normalmente viene interrogato in modo tale da produrre venti transazioni per unità di tempo. È progettato per sopportare fino a trenta transazioni.
• - Test di sicurezza : il sistema può essere usato in condizioni non corrette, ad esempio si sottopone a delle violazioni, anche di tipo accidentale.
• - Test di robustezza : si forniscono al sistema dei dati sbagliati, e si osserva il comportamento del sistema rispetto a tali dati. (esempio tipico: si digitano sequenze casuali di tasti sulla tastiera per controllare se l’interfaccia utente di un programma si “pianta”)
42
Test di sistema
• In alcuni casi, si usano strumenti specifici per il test di sistema;
ad esempio quando il codice software viene caricato su un microprocessore dedicato a qualche funzione di controllo (sistema embedded), si ricorre all’uso di strumenti di simulazione dell’ambiente esterno al processore, o all’uso di emulatori su macchina host della macchina target su cui verra’ caricato il software, o ai cosiddetti In-circuit emulator, che permettono alla macchina host di emulare il comportamento di un microprocessore target, ricevendone - tramite un apposito connettore che si sostituisce al processore sulla scheda del sistema target - direttamente gli input reali, e producendo valori in uscita direttamente al sistema.
43
Test di accettazione
Nel test di sistema il software viene confrontato con la specifica dei requisiti (verifica).
Questo test e` normalmente responsabilita` dello sviluppatore.
Nel Test di accettazione il software viene confrontato con i requisiti dell’utente finale (validazione)
Questo test e` normalmente svolto dal cliente.
Una caratteristica del test di accettazione e` che viene usualmente svolto senza avere a disposizione il codice sorgente
Per prodotti di largo consumo si utilizzano i concetto di -test e -test.
44
Test di accettazione
-test: il software viene già usato all’interno della casa produttrice per verificarne le funzionalità, però ancora non è stato rilasciato all’esterno
-test: il software viene rilasciato a un
numero selezionato di utenti che lo usano sapendo che non è una versione stabile, e che possono interagire con chi ha prodotto il software.
45
Test di regressione
• Testing di una nuova release: come si puo’ minimizzare lo sforzo usando risultati del testing di versioni precedenti?
Mantenere i drivers, gli stubs, gli oracoli ememorizzare i test cases della vecchia versione
Nel corso del testing della nuova versione:Tenere traccia dei cambiamentiValutare l’impatto dei cambiamenti
Strumenti di supporto al testing di regressione
46
Problema dell’incrementalita` dei test:
• Sarebbe auspicabile che il costo di ritestare un programma dopo una piccola modifica sia marginale, e non paragonabile al primo testing del programma
• I criteri di copertura strutturale non sono incrementali: le percentuali di copertura possono venir sconvolte da una piccola modifica.
47
Analisi mutazionale • Dopo aver esercitato P su T, si verifica P corretto rispetto a T.
Si vuole fare una verifica più profonda sulla correttezza di P: introduco degli errori ( piccoli ) su P e chiamo il programma modificato P’. Questo P’ viene detto mutante. Si eseguono su P’ gli stessi test di T. Il test dovrebbe rilevare gli errori. Se il test non rileva questi errori, allora significa che il test non era valido. Questo è un metodo per valutare la capacità di un test, e vedere se è il caso di introdurre test più sofisticati.
Fault based testing Software Fault injection
48
Object-Oriented Testing
Caratteristiche principali del paradigma object oriented:
• Gli oggetti hanno uno stato, accessibile solo attraverso le loro interfacce
• Ereditarieta`: nuove classi possono essere derivate per specializzazione di classi esistenti
• Polimorfismo e dynamic binding: le invocazioni dei metodi possono essere legate dinamicamente a codice diverso
• Genericita`
49
Problemi per il testing
• 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 • Ereditarieta`: I metodi sono riutilizzati in classi diverse • Genericita` : Le classi generiche devono essere instanziate per essere testate
50
Object Oriented : ereditarieta`
Programmazione Procedurale: Il codice e` strutturato in subroutines e moduli I moduli sono composti in modo bottom-up o top-down Quando una subroutine e` stata testata, non lo deve essere piu`
Programmazione Object oriented: Il codice e` strutturato in classi. L’ ereditarieta` e` la relazione fondamentale tra classi L’ ereditarieta` favorisce il riuso e lo sviluppo incrementale:alcune operazioni non sono modificate nelle sotto-classi, altre sono ridefinite o cancellate
Problemi: Come fare test incrementale? Quali operazioni ereditate devono essere ri-testate?
51
Problemi dovuti all’ereditarieta`class Shape{ private: Point reference_point; public: void put_reference_point(Point); point get_reference_point(); void move_to(Point); void erase(); virtual void rotate(int); virtual void draw() = 0; virtual float area(); shape(point); shape(); }
class Circle : public Shape{ private: int radius; // new attribute public: void rotate(int); // redefined void draw(); // redefined void circle(point p, int r); }
Il metodo Circle::move_to deve essere testato?
52
Possibili approcci Flattering inheritance: ogni sottoclasse viene testata come
se tutte le caratteristiche ereditate fossere definite ex-novo
I tests usati nelle super-classi possono essere riusati Molti tests sono ridondanti
Testing incrementale: ridurre i tests solo alle features nuove o modificate
• Si basa sulla storia del testing • Insieme di tests ridotto
53
Caratteristiche Object Oriented: la Genericita`
Programmazione Procedurale:
Non ci sono moduli generici (con l’eccezione di Ada) Programmazione Object oriented:
I moduli generici sono presenti nella maggior parte dei linguaggi OO
Concetto basilare per la costruzione di librerie di componenti riusabili
Problemi:
Quali assunzioni si possono fare sul parametro?
Quale metodo usare nel testing di un componente generico gia` riutilizzato?
54
Problemi dovuti alla Genericity
template vector{ T* v; int sz; public: vector(int); void sort(); ... } vector(complex) complex_vector(100); vector(int) integer_vector(100);
Quali assunzioni devono essere fatte su int e complex per chiamare il corrispondente metodo sort?
55
Caratteristiche Object Oriented:
Polimorfismo e dynamic binding
Programmazione Procedurale:
Le chiamate di procedura sono legate staticamente
Programmazione Object oriented:
Un entita` puo’ riferire oggetti di classi diverse di una gerarchia. Molte implementazioni di un operazione?
La selezione dell’effettivo codice da chiamare e’ posposta a run-time
Problemi:
Come coprire tutte le chiamate alle operazioni polimorfiche?
Come esercitare tutte le implementazionidi un’operazione?
Come trattare parametri polimorfici?
Il testing strutturale non puo` essere determinato staticamente
56
Un esempio di Problema dovuto al Dynamic Binding
void foo(Shape polygon) { ... data = polygon.area; ... }
Polygon puo’ essere un triangolo, un pentagono, un quadrilatero
Quale implementazione di area viene effettivamente chiamata?
57
Possibili approcci
Tutte le possibili combinazioni di chiamate polimorfiche e parametri polimorfici rischiano di fare esplodere in modo combinatorio il numero dei casi di test.
Si puo’ ottenere una riduzione di questo effetto attraverso tecniche di analisi statica che permettano di individuare le effettive chiamate delle unita` interessate (data-flow analysis)
58
Tecniche tradizionali che funzionano anche per OO
• I test di sistema e di accettazione non sono influenzati, perche’ si basano sui requisiti funzionali.
• Gli approcci tradizionali al testing strutturale possono essere utilizzati per testare metodi singoli.
• Il test di regressione richiede solo di scrivere nuovi test per le caratteristiche nuove o modificate
• L’esecuzione dei test richiede solo modifiche sintattiche
59
Altre tecniche di verifica
• Analisi statica– Analisi lessicale– Analisi sintattica– Controllo dei tipi– Analisi del flusso dei dati (data flow analysis)
•Ispezione/ Review/ Walkthrough