cerca
Ingegneria del Software - Appunti dell'12 Maggio 2009
modifica cronologia stampa login logout

Wiki

UniCrema


Materie per semestre

Materie per anno

Materie per laurea


Help

Ingegneria del Software - Appunti dell'12 Maggio 2009

Torna alla pagina di Ingegneria del Software

 :: Ingegneria del Software - Appunti dell'12 Maggio 2009 ::

Quest'oggi facciamo un po' di esercizi sul testing.

Esercizio tipico

Una domanda classica da esame consiste nel somministrarci del codice, e chiederci come genereremmo i casi di test senza sapere niente della semantica, oppure come li genereremmo sapendo invece qualcosa della semantica.

Per esempio, ci viene dato questo codice:

 void swap (int &x, int &y)
 {
     /*swaps x,y if x > y*/
     ...
 }

Beh, il codice non c'è, però abbiamo un commento che ci dice che cosa dovrebbe fare quella funzione.

Testing guardando solo l'interfaccia

L'interfaccia del metodo mi dice che prende in ingresso due interi, o meglio, due reference ad interi. Il metodo della suddivisione uniforme dello spazio di input vuole che noi grigliamo a dovere lo spazio di input, e prendiamo dei valori tendenzialmente distribuiti sui bordi di tale spazio. Ovviamente non è il massimo della furbizia.

Testing fatto da un operatore umano che legge il commento

Un essere umano è in grado di leggere il commento e di trarre informazioni sul contenuto. Capirebbe subito che il metodo uniforme non serve quasi a niente, e allora decide di suddividere i suoi 10 test che ha il tempo di fare così:

  • 5 test in cui la condizione x > y è soddisfatta
  • 5 test in cui la condizione x < y non è soddisfatta

Ovviamente, fa sempre comodo prendere i valori sul boundary, ma tutti gli altri casi di test li prende con un occhio alla condizione espressa nel commento.

Testing fatto da un generatore automatico

Esistono anche i generatori automatici di test. Questi prendono delle precondizioni scritte in un linguaggio formale, e traggono automaticamente dei casi di test. In questo esempio, non ci sono, e quindi ci attacchiamo.

Nota informativa sintetica

Finora siamo stati in grado di prendere la "metà" dello spazio di input o simili cose perché lo spazio di input stesso era costituito da numeri. Nel caso in cui i tipi in ingresso non siano ordinali, che ordinamento posso prendere? La risposta semplice e funzionale non c'è. Elenchiamo tuttavia alcuni metodi:

  • uso la rappresentazione binaria del tipo per fare ordinamento
  • uso alcuno valori numerici contenuti nel tipo

ma si capisce subito che non sono soluzioni generali al problema.

Esercizio 3, compito del 23 gennaio 2008

Abbiamo una funzione boolean Bisestile(anno) la quale ci dice se un anno è bisestile o no.

Per sapere se l'anno è bisestile, queste sono le regole:

  • l'anno deve essere divisibile per 4 ma NON per 100
  • OPPURE l'anno deve essere divisibile per 400

Siccome ho tutte queste belle informazioni sulla definizione del metodo, non vado a cercare il boundary, ma cerco di sfruttare queste condizioni. Vediamole in forma più matematica:

 ((x % 4 == 0) AND (x % 100 != 0)) OR (x % 400 == 0)

Ho due decisioni:

  • (x % 4 == 0) AND (x % 100 != 0)
  • (x % 400 == 0)

e 3 condizioni, che sarebbero le singole parentesi.

Per testare il tutto, dovremmo prendere almeno i seguenti casi di test:

  • un caso di test che soddisfi (x % 4 ==0) AND (x % 100 != 0)
  • un caso di test che soddisfi (x % 4 == 0) MA NON (x % 100 != 0)
  • un caso di test che soddisfi (x % 100 != 0) MA NON (x % 4 == 0)
  • un caso di test che non soddisfi né (x % 4 == 0) né (x % 100 != 0)
  • un caso di test che combini le non-soddisfazioni della prima decisione con, rispettivamente, la soddisfazione e la non-soddisfazione della seconda decisione, che sarebbe quella che dice (x % 400 == 0)

Questo, in linea teorica.
Dal punto di vista pratico, possiamo osservare che se un numero non è divisibile per 100, non lo sarà nemmeno per 4, e nemmeno per 400. Le condizioni non sono totalmente indipendenti tra di loro.

Pertanto, possiamo scegliere:

  • 1937: non è bisestile e non soddisfa nessuno
  • 1988: è bisestile per la prima condizione ma non per la seconda
  • 1900: è bisestile per via della divisibilità per 400, cioè la seconda condizione, ma non la prima perché è divisibile sì per 4 ma anche per 100

Per avere qualche indizio in più relativo al procedimento da seguire in questi frangenti si può consultare la pagina di LPS dedicata al testing.

Implementazione del metodo (esercizio 3)

Il metodo bisestile è implementato così:

 if (a % 400 == 0) 
 {
     if (a % 100 != 0) return true;
     else return false;
 } 
 else if (a % 100 ==0) return false;
 else if (a % 4 == 0) return true;
 else return false;

Un po' convoluto.

L'esercizio ci chiede:

  • lo statement coverage al 100%
  • il path coverage al 100%

Lo statement coverage al 100% è irraggiungibile. La risposta va motivata, ed eccone la ragione: se un numero È divisibile per 400, il primo if è soddisfatto, ma sicuramente NON sarà soddisfatto l'if immediatamente successivo, quello che dice if (a % 100 != 0), perché se un numero è divisibile per 400 allora lo è anche per 100. Pertanto, l'istruzione return true di quella riga non verrà mai eseguita.

Il path coverage invece lo posso avere al 100%: infatti, i cammini esecutivi sono quelli effettivamente esguibili. Quelli visti sopra non sono eseguibili, ma tutti gli altri li posso eseguire tranquillamente.

Il codice che non può mai essere raggiunto si chiama dead code, e la ragione della sua esistenza si deve alla presenza di una condizione interna che nega una condizione presente sul cammino compiuto per arrivare ad essa.


Torna alla pagina di Ingegneria del Software