Vai alla Home Page About me Courseware Federica Living Library Federica Federica Podstudio Virtual Campus 3D Le Miniguide all'orientamento Gli eBook di Federica La Corte in Rete
 
I corsi di Ingegneria
 
Il Corso Le lezioni del Corso La Cattedra
 
Materiali di approfondimento Risorse Web Il Podcast di questa lezione

Porfirio Tramontana » 21.Testing White Box


Testing Strutturale (White Box)

Il Testing White Box è un testing strutturale, poichè utilizza la struttura interna del programma per ricavare i dati di test.

Tramite il testing White Box si possono formulare criteri di copertura più precisi di quelli formulabili con testing Black Box.
Test White Box che hanno successo possono fornire maggiori indicazioni al debugger sulla posizione dell’errore.

Control-Flow Graph

Il grafo del flusso di controllo (Control-Flow Graph) di un programma P: CFG (P) = <N, AC,nI, nF>

dove:
<N, AC> è un grafo diretto con archi etichettati,
{nI, nF} ⊆ N, N- {nI, nF} = Ns ∪ Np
Ns e Np sono insiemi disgiunti di nodi istruzione e nodi predicato;
AC ⊆ N-{nF}x N-{nI } x {vero, falso, incond} rappresenta la relazione flusso di controllo; nI ed nF sono detti rispettivamente nodo iniziale e nodo finale.

Un nodo n∈ Ns ∪ {nI} ha un solo successore immediato e il suo arco uscente è etichettato con incond.

Un nodo n∈Np ha due successori immediati e i suoi archi uscenti sono etichettati rispettivamente con vero e falso.

Proprietà dei nodi

Il nodo iniziale è l’unico senza archi entranti.
Il nodo finale è l’unico senza archi uscenti.
Un nodo istruzione ha un solo arco uscente.
Un nodo predicato ha due archi uscenti, etichettati dai valori true e false.

Un nodo istruzione o predicato deve avere almeno un arco entrante.
Non c’è limite superiore al numero di archi entranti in un nodo istruzione o predicato.

Strutture di controllo e CFG

I nodi 2, 4, 6, 8 potevano anche essere omessi, dato che ad essi non corrisponde alcuna istruzione esecutiva, nel codice eseguibile del programma.
Uno switch può essere risolto trasformandolo (come fa il compilatore) in una serie di if else in cascata.

I nodi 2, 4, 6, 8 potevano anche essere omessi, dato che ad essi non corrisponde alcuna istruzione esecutiva, nel codice eseguibile del programma. Uno switch può essere risolto trasformandolo (come fa il compilatore) in una serie di if else in cascata.


Strutture di controllo e CFG (segue)


Ciclo For e CFG

Il CFG a destra esprime più precisamente la semantica del ciclo for, così come viene trasformato in codice oggetto da un compilatore C. Infatti ad ogni ciclo for corrisponde un init preventivo, una decisione all’inizio di ogni ciclo (come in un while) e un codice di continuazione cont (ad esempio i++) da ripetere prima di ricominciare il ciclo.

Il CFG a destra esprime più precisamente la semantica del ciclo for, così come viene trasformato in codice oggetto da un compilatore C. Infatti ad ogni ciclo for corrisponde un init preventivo, una decisione all'inizio di ogni ciclo (come in un while) e un codice di continuazione cont (ad esempio i++) da ripetere prima di ricominciare il ciclo.


Osservazione

Per semplicità, il CFG è stato studiato rispetto ad un linguaggio di programmazione di alto livello (in questo caso C).
In realtà, il CFG dovrebbe modellare l’effettiva esecuzione di un programma, quindi il suo codice macchina.
Nella trasformazione di un programma nel suo CFG corrispondente bisognerebbe sempre considerare il risultato della sua compilazione (come nel caso del for).

Decisioni e condizioni

Una decisione corrisponde ad una espressione booleana oggetto di un costrutto.
Ad una decisione corrisponde un nodo predicato con due uscite, corrispondenti ai valori vero e falso.
Volendo scendere più nel dettaglio, una decisione può essere scritta come una combinazione di condizioni booleane. Ad esempio:
(a>0) and (b<0) or (c>d)
È una decisione composta di tre condizioni.

In linguaggio macchina, esistono istruzioni che elaborano singole condizioni piuttosto che complesse decisioni.
Per massimizzare la fedeltà del CFG al programma che verrà eseguito, bisognerebbe considerare un nodo predicato per ogni condizione.
Problema: non sempre è possibile conoscere in che ordine le condizioni vengano considerate dal compilatore.

Esempio

int Quadrato (int x, int y){
1. read(x);
2. if (x > 0){
3. n = 1;
4. y = 1;
5. while (x > 1) {
6. n = n + 2;
7. y = y + n;
8. x = x - 1;
}
9. write(y);
}
}

Possono essere raggruppate quelle istruzioni che verranno sempre eseguite in sequenza.
Inoltre non deve esserci alcuna freccia che porta ad una istruzione diversa dalla prima di quelle raggruppate.
Ad esempio, 5 non può essere raggruppata con 3 e 4.


Criteri di copertura

Copertura dei comandi (statement test)

  • Richiede che ogni nodo del CFG venga eseguito almeno una volta durante il testing;
  • è un criterio di copertura debole, che non assicura la copertura sia del ramo true che false di una decisione.

Es. nella Procedura quadrato, la test suite TS={x=2} garantisce la copertura di tutti i nodi, ma non dell’arco (2, Fine).

Criteri di copertura (segue)

Copertura delle decisioni (branch test).
Richiede che ciascun arco del CFG sia attraversato almeno una volta;
In questo caso ogni decisione è stata sia vera che falsa in almeno un test case.

Nella procedura Quadrato, la TS={x=2, x=-1} garantisce la copertura delle decisioni.
Un limite è legato alle decisioni in cui più condizioni (legate da operatori logici AND ed OR) sono valutate.

Copertura delle condizioni (condition test)

Ciascuna condizione nei nodi decisione di un CFG deve essere valutata almeno una volta sia per valori true che false.

Esempio:
int check (x);// controlla se un intero è fra 0 e 100
int x;
{ if ((x>=0) && (x<= 200))
check= true;
else check = false;
}

TS1={x=5, x=-5 } valuta la decisione sia per valori True che False, ma non le condizioni (la seconda condizione è sempre true).
TS2={x= -3, x=210} è una Test suite che copre tutte le condizioni ma non tutte le decisioni (la and è in entrambi i casi complessivamente false).

Copertura delle combinazioni delle condizioni

Per garantire contemporaneamente sia la copertura di tutte le condizioni che di tutte le decisioni, bisogna cercare di coprire tutte le combinazioni delle condizioni.
Es. If (x>0 && y>0) …
TS1={(x=-1, y=-1), (x= 2, y=3), (x=5, y=-4), (x=-3, y=4)} copre tutte le combinazioni delle condizioni.

Problema:
Nella condizione dell’esempio precedente (x>=0 && x<=200) non tutte le combinazioni di condizioni sono verificabili (la combinazione false, false non è ottenibile).

Cammini linearmente indipendenti (McCabe)

Un cammino è un’esecuzione del modulo dal nodo iniziale del Cfg al nodo finale.
Un cammino si dice indipendente (rispetto ad un insieme di cammini) 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 forma 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.

Numero di cammini linearmente indipendenti

Il numero dei cammini linearmente indipendenti di un programma è pari al numero ciclomatico di McCabe:

  • V(G) = E – N +2
    • Dove E: n.ro di archi in G – N: n.ro di nodi in G
  • V(G) = P + 1
    • Dove P: n.ro di predicati in G
  • V(G) = n.ro di regioni chiuse in G + 1

Test case esercitanti i cammini di base garantiscono l’esecuzione di ciascuna istruzione almeno una volta e anche il percorrimento di ogni arco.

Esempio


Criteri di copertura dei cammini

Copertura dei cammini (path test)

  • spesso gli errori si verificano eseguendo cammini che includono particolari sequenze di nodi decisione;
  • non tutti i cammini eseguibili in un CFG possono essere eseguiti durante il test (un CFG con loop può avere infiniti cammini eseguibili).

Copertura dei cammini indipendenti

  • ci si limita ad eseguire un insieme di cammini indipendenti di un CFG, ossia un insieme di cammini in cui nessun cammino è completamente contenuto in un altro dell’insieme, nè è la combinazione di altri cammini dell’insieme;
  • ciascun cammino dell’insieme presenterà almeno un arco non presente in qualche altro cammino;
  • il numero di cammini indipendenti coincide con la complessità ciclomatica del programma.

Relazioni tra i criteri di copertura

La copertura delle decisioni implica la copertura dei nodi.
La copertura delle condizioni non sempre implica la copertura delle decisioni e la copertura dei nodi.
La copertura dei cammini linearmente indipendenti implica la copertura dei nodi e la copertura delle decisioni.
La copertura dei cammini è un test ideale ed implica tutti gli altri.

Problemi

È possibile riconoscere automaticame.nte quale cammino linearmente indipendente viene coperto dall’esecuzione di un dato test case

È indecidibile il problema di trovare un test case che va a coprire un dato cammino;
Alcuni cammini possono risultare non percorribili (infeasible).

La copertura dei cammini linearmente indipendenti non garantisce da errori dovuti, ad esempio, al numero di cicli eseguiti, per i quali sarebbe necessaria la copertura di tutti I cammini, che però rappresenta il testing esaustivo!

Esempio

1. #include
2. using namespace std;
3. int main ()
4. {
5. char risposta;
6. cout<<"\n Vuoi trovare il massimo fra tre interi? (Inserisci N o n per finire)"; cin>> risposta;
7. while(risposta!='N' && risposta!='n')
8. {
9. int num1, num2, num3;
10. cout<<"\n Inserisci primo numero: "; cin>> num1;

Esempio (segue)

11. cout<<"\n Inserisci secondo numero: "; cin>> num2;
12. cout<<"\n Inserisci terzo numero: "; cin>> num3;
13. int max = num1;
14. if ( num2 > max )
15. max = num2;
16. if ( num3 > max )
17. max = num3;
18. cout<<"\n Il massimo e':"<<<"\n";
19. cout<<"\n Vuoi continuare? (Inserisci N o n per finire)"; cin>> risposta;
20. }
21. system("Pause");
22. return(0);
23. }

Testing White box

a) Disegnare il CFG della funzione e calcolarne la complessità ciclomatica;
Progettare un insieme di casi di test (ognuno costituito da un insieme di valori per le variabili in input) in grado di coprire:
b) tutti le istruzioni del programma;
c) tutte le decisioni del programma;
d) tutte le condizioni del programma;
e) tutte le condizioni e decisioni;
f) tutti i cammini linearmente indipendenti.

CFG


I Casi di Test


Copertura dei cammini indipendenti


I materiali di supporto della lezione

Sommerville, Ingegneria del Software, 8° edizione, Capitoli 22-23-24.

A. Cimitile, introduzione al testing (slides)

G.A. Di Lucca, testing (slides)

  • Contenuti protetti da Creative Commons
  • Feed RSS
  • Condividi su FriendFeed
  • Condividi su Facebook
  • Segnala su Twitter
  • Condividi su LinkedIn
Progetto "Campus Virtuale" dell'Università degli Studi di Napoli Federico II, realizzato con il cofinanziamento dell'Unione europea. Asse V - Società dell'informazione - Obiettivo Operativo 5.1 e-Government ed e-Inclusion