Il testing si può fare in tanti modi diversi, per esempio a partire dalle specifiche, e i principali vantaggi sono la possibilità di automatizzarlo, generando i casi di test per esempio dalle specifiche algebrice, automi a stati finiti, grammatica... generando tutte le possibili frasi o combinazioni di dati.
Quando abbiamo specifiche meno formali (semi formali) per esempio basate su diagrammi il cui significato va immaginato da chi legge, potremmo avere problemi di automatizzazione o incomprensioni.
Si basa sulla copertura strutturale, che è un criterio di adeguatezza: quando si raggiunge il 100% abbiamo finito.
Prevede che ogni istruzione eseguibile venga testata almeno una volta. Lo svantaggio è che se c'è un errore il testing lo può rilevare soltato una volta che arrivo a testare quella determinata istruzione con quel determinato caso di test.
Realisticamente è un ottimo risultato arrivare a circa l'85% di coverage.
Per facilitare il lavoro si utilizza il grafo di flusso del controllo (control flow graph) che prende un programma e lo trasforma in un grafo. Con questo strumento possiamo vedere graficamente i percorsi ancora da testare.
Esempio:
Basic Block Coverage: Una buona cosa da fare è massimizzare la percentuale di blocchi "base", ovvero blocchi di codice di istruzioni sequenziali, che quindi verranno eseguite una dopo l'altra (→ evitare diramazioni inutili).
L'idea è che non ci basta coprire tutte le istruzioni, ma dobbiamo coprire anche tutte le diramazioni (nell'esempio non abbiamo coperto le diramazioni nere, e per coprirle bisognerebbe testare un secondo caso di test).
Verosimilmente avremo un insieme di casi di test, detto test suite. Oppure a volte può bastare un solo input di test che copre tutte le diramazioni, tuttavia questa ipotesi è poco probabile.
Esempio (continua l'esempio precedente):
Tuttavia anche in questo caso, resta scoperto il test sulla seconda parte del primo if (a[i]<x
). Per avere una copertura completa servirebbero quindi 3 test di input.