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

Wiki

UniCrema


Materie per semestre

Materie per anno

Materie per laurea


Help

Ingegneria del Software - Appunti dell'11 Maggio 2009

Torna alla pagina di Ingegneria del Software

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

Testing

Partiamo subito informando che:

  • il testing funzionale si effettua principalmente con tecniche blackbox; il criterio di copertura qui è dato dal numero di requisiti che un test copre;
  • il testing non funzionale si effettua principalmente con tecniche whitebox, e il criterio di copertura è dato dalla percentuale di codice eseguito durante il test stesso.

C'è però un particolare interessante, relativo al testing funzionale. Un conto è dire che tutti i requisiti sono stati coperti; ma stiamo attenti che potremmo aver testato un requisito con solo pochi valori dello spazio di input, o magari solo uno! Dire che tutti i requisiti sono stati coperti pertanto non è sufficiente a garantire la bontà di un test.

Redigere il test plan

Il test plan, come abbiamo già visto, è l'insieme di casi di test che mi assicura un certo livello di copertura.

Nel processo di sviluppo a cascata, il testing è l'ultima fase della vita del software. Lo stesso accade per i processi di sviluppo life-cycle oriented, ovvero quelli le cui fasi rispecchiano le fasi di vita del prodotto software: il testing è una fase, e nel caso di processi iterativi sarà ripetuta ad ogni iterazione.

Invece, con i processi di sviluppo non life-cycle oriented, l'attività di testing è distribuita durante tutto il processo di testing, in modo vario ed eventuale. Questo va tenuto in conto durante la redazione di un test plan.

Elementi fondamentali di un test plan

L'introduzione di un test plan ci deve dire:

  • lo scopo del test in questione
  • la sua descrizione, cioè a che punto del processo si inserisce il test

L'ambiente di testing comprende l'elenco di tutti i driver e tutti gli stub necessari per eseguire questo test. Ricordiamo che i driver e gli stub possono essere anche molto costosi.

Occorre poi una descrizione del caso di test, cioè i criteri di copertura seguiti, e qualche dettaglio relativo all'esecuzione del file stesso:

  • un qualche identificativo univoco del test
  • lo scopo del test
  • il valore o l'insieme di valori usati come test
  • il risultato atteso
  • il risultato effettivo
  • il livello di priorità del test stesso

Chiaramente, se il risultato effettivo differisce da quello atteso, allora è grama.

Infine, occorre redigere anche un po' di conclusioni, cioè tirare un po' le somme sui risultati del mio test.

Altri elementi di un test plan

Oltre ai punti elencati sopra, è possibile completare il test plan con le seguenti informazioni:

  • processo di test usato
  • riferimenti ai requisiti che si stanno testando
  • tested items, ovvero quale prodotto è oggetto del test (ovvero, quale versione di quella classe o libreria o quello che è sto testando: ricordiamo che il software cresce e si sviluppa e può modificare i suoi comportamenti)
  • schedule di test, ovvero quando eseguire il test e con che risorse
  • test record, ovvero un formato di memorizzazione del test e dei suoi risultati
  • i requisiti hw e sw necessari per eseguire il testing
  • eventuali vincoli nell'esecuzione del test

Per quanto riguarda lo schedule, bisogna aggiungere un'osservazione relativa alle responsabilità del testing. Esse si dividono in due parti:

  • la responsabilità dell'estensore del caso di test
  • la responsabilità dell'esecutore (somministratore) del caso di test

Certo, spesso l'estensore e l'esecutore sono la stessa persona, ma si tratta comunque di due aspetti concettualmente separati.

Come scegliere i casi di test

Abbiamo barbellato a lungo sui criteri di copertura, ed è giunta l'ora di vederne un po'.

Criterio di copertura uniforme dello spazio di input

Sappiamo già bene che non è possibile in pratica coprire tutto lo spazio di input, anche per i casi più semplici. Potremmo decidere di generare a caso valori di input, ma per essere un pochettino più furbi utilizziamo invece una distribuzione uniforme dei casi di test rispetto allo spazio di input.

Ad esempio, se ho a disposizione 10 minuti, e ogni minuto riesco a fare 6000 test, allora dividerò lo spazio di input complessivo in 6000 * 10 parti. Se la funzione prende come valore in input un intero (232), allora faccio 2'^32 / 60000, e di ciascuna "fettina" così ricavata prendo il valore centrale.

Valori tipici e critici

Tuttavia, usare pedissequamente la copertura uniforme non ci permette di mettere nella dovuta considerazione la faccenda dei valori tipici e critici, vista nella precedente lezione.

Per sistemare questo problema, stabiliamo che se la nostra fettina dello spazio in input è una fettina che sta sul bordo, allora considereremo più importanti i valori sul bordo piuttosto che quelli che stanno in mezzo alla fettina. Se invece la fettina sta nel mezzo, allora ci comportiamo come prima ed utilizziamo il suo punto centrale.

Dobbiamo stare tuttavia attenti a non concentrare tutto lo sforzo sui valori del boundary, perché potrebbero essere veramente tanti ed esaurire il tempo a nostra disposizione.

Anche prima parlavamo di tempo: nel mondo reale c'è solo un tempo limitato che si può dedicare al testing, e pertanto va sfruttato nel modo più intelligente possibile.

Sfruttare le pre- e le post-condizioni

Durante la fase di modellazione potrei aver avuto modo di stabilire, per ogni metodo, delle precondizioni e delle postcondizioni. Le precondizioni sono quelle condizioni che devono valere prima della chiamata di un metodo. Le postcondizioni sono quelle condizioni che invece devono valre dopo l'esecuzione di un metodo.

Quando stabilisco a priori queste condizioni, si dice che faccio una verifica del modello. Controllare queste condizioni a posteriori si chiama invece validazione. Le persone che si occupano di generare modelli dotati di semantica (cioè, in ultima analisi, di pre- e postcondizioni) in questo caso possono essere le stesse che genereranno il test plan, proprio perché sfrutto le informazioni che il modello mi dà per generare casi di test. L'attività si chiama allora Verification and Validation, V&V in breve.

All'atto pratico, utilizzerò come casi di test quei valori che generano una violazione delle precondizioni, perché sarà lì che il mio codice presumibilmente cascherà.

Test non funzionali

All'inizio dicevamo che i test non funzionali si eseguono perlopiù con tecniche whitebox, cioè tecniche che applicano criteri di copertura basati sul codice scritto.

Il criterio di copertura si può definire come il numero di tracce esecutive che il mio caso di test esegue con un certo valore, rispetto al numero di tracce esecutive totale.

Una traccia esecutiva è un "percorso" all'interno del mio codice. Se immaginiamo il nostro codice sotto forma di diagramma di flusso, allora una traccia esecutiva è un percorso in questo diagramma che parte dall'inizio e arriva alla fine. Una traccia può coinvolgere o meno tutte le parti di questo diagramma, e proprio la quantità di questa copertura è il mio criterio.

In realtà siamo un po' più specifici, e consideriamo questi aspetti di copertura del codice:

  • statement coverage = quante istruzioni singole sono state eseguite
  • loop coverage = quanti loop ho eseguito
  • branch coverage = quante branch, ovvero rami di una condizione, ho coperto
  • path coverage = quante tracce esecutive ho percorso

Spesso queste definizioni sono intercambiabili: ad esempio, una copertura completa delle branch è sicuramente una copertura completa del codice. Tuttavia servono per focalizzarsi meglio sulla parte di codice che voglio testare.

Altri tipi di test non funzionali

Non si testa solamente il codice, ci sono anche altri requisiti non funzionali che possono essere oggetto di testing.

  • configuration testing = si cambiano le configurazioni del programma, e si vede se si comporta ancora bene
  • recovery testing = si verifica il comportamento del sw dopo un crash e successivo recovery
  • safety testing = si verifica se il programma rispetta i permessi dei dati che manipola
  • security testing = si verifica se il programma resiste ad attacchi maliziosi
  • stress testing = si verifica il comportamento del programma nelle condizioni di lavoro più onerose, dal punto di vista delle performance
  • performance testing = si verifica il comportamento del programma, sempre dal punto di vista delle performance, ma nel caso di carico di lavoro medio

Test di utilità

Un test di utilità mi dice quanto il prodotto soddisfa le esigenze dell'utente, così come previsto dalle specifiche.

Sicuramente, implica la completezza funzionale: se non fa quello che deve, allora sicuramente non gli piace. Ma occorre anche vedere come fa quello che deve fare.

Si deve quindi anche considerare la facilità d'uso, o l'accessibilità, tenendo a mente che questa dipende da chi utilizzerà il programma. Gli utenti potranno essere diversi, e potranno avere una cultura diversa, attitudini diverse, approcci diversi allo stesso problema, pur utilizzando lo stesso software. Ci si aspetta che il nostro prodotto rispetti queste differenze interne all'ambiente lavorativo in cui sarà installato

Infine, c'è da valutare anche il rapporto costi/funzionalità.

Note sulla correttezza di un programma

Un sw è corretto quando, usato secondo le specifiche, soddisfa le specifiche stesse.

Qui però salta fuori la distinzione tra fault e failure. Prendiamo ad esempio il seguente frammento di codice C:

 if (c = 0) { ... }

La condizione è stata scritta c = 0, cioè si tratta di un assegnamento, il cui risultato sarà sempre true.

Se l'utilizzo solito del programma prevede che a quel punto la variabile c valga effettivamente 0, allora non ci accorgeremo mai dell'errore, perché si comporta sempre come dovrebbe! Se infatti c valesse 0, la condizione corretta c == 0 darebbe true, esattamente come già dà ora l'assegnamento c = 0. Pertanto non ho una failure, pur avendo un fault nel codice. La failure è in agguato nell'oscurità!


Torna alla pagina di Ingegneria del Software