Le informazioni utilizzate nella progettazione dei test vengono raccolte da varie fonti: modelli di progettazione,
interfacce del classificatore, diagrammi di stato e il codice stesso. In un determinato momento, queste informazioni di
origine sul documento devono essere convertite in test eseguibili:
-
input specific assegnati al software sottoposto a test
-
una particolare configurazione hardware e software
-
inizializzato in uno stato noto
-
previsti risultati specifici
E' possibile andare direttamente dalle informazioni del documento di origine ai test eseguibili, ma spesso è utile
aggiungere una fase intermedia. In questa fase, le proposte di test vengono scritte in un elenco di proposte di test,
utilizzato per creare test eseguibili.
Una proposta di test (talvolta indicata come requisito del test) è una breve istruzione relativa a un test che è possibile
eseguire. Come semplice esempio, considerare una funzione che calcola una radice quadrata e creare delle proposte di
test:
-
fornire un numero leggermente inferiore a zero rispetto all'input
-
fornire zero come input
-
testare un numero che sia una radice quadrata perfetta, come 4 o 16 (il risultato è esattamente 2 o 4?)
Ognuna di tali idee può essere convertita prontamente in un test eseguibile con descrizioni esatte di input e dei
risultati previsti.
Esistono due vantaggi in questa forma intermedia meno specifica:
-
le proposte di test sono più semplici da esaminare e più comprensibili rispetto ai test completi; è più semplice
capire il ragionamento che si cela dietro tali proposte
-
le proposte di test supportano dei test più potenti, come descritto in seguito nell'intestazione Progettazione dei test tramite l'elenco
Tutti gli esempi relativi alla radice quadrata descrivono gli input, ma le proposte di test possono descrivere
qualsiasi elemento di un test eseguibile. Ad esempio "stampa su una LaserJet IIIp" descrive un aspetto dell'ambiente di test da utilizzare per un test, come avviene per "test
con database pieno", tuttavia, queste ultime proposte di test sono estremamente vaghe in se stesse: cosa si
stampa nella stampante? Cosa si fa con il database completo? Esse, tuttavia, assicurano che non vengano
dimenticate delle proposte importanti; proposte che verranno descritte in dettaglio in un secondo momento della
progettazione di test.
Le proposte di test si basano spesso su modelli di errore; nozioni relative agli errori plausibili nel software e
alle modalità con cui è possibile individuare tali errori. Ad esempio, considerare i boundary. E' consigliabile
presupporre che la funzione di radice quadrata possa essere implementata in un modo simile al seguente:
double sqrt(double x) {
if (x < 0)
// errore di segnale
...
E' inoltre plausibile che < venga scritto in modo non corretto, ossia come <=. Questo errore è
molto frequente, quindi vale la pena controllare. Non è possibile individuare l'errore se X assume il valore
2, perché sia l'espressione sbagliata (x<=0) che l'espressione corretta (x<0) utilizzeranno
lo stesso ramo dell'istruzione if. In modo simile, se si assegna ad X il valore -5, non è
possibile individuare l'errore. L'unico modo per rilevarlo è assegnare ad X il valore 0, che giustifica
la seconda proposta di test.
In questo caso, il modello di errore è esplicito. In altri casi, è implicito. Ad esempio, ogni qualvolta un programma
gestisce una struttura collegata, è consigliabile eseguire nuovamente il test rispetto a una struttura circolare. E'
possibile che molti errori conducano a una struttura circolare mal gestita. Per gli scopi di verifica, non è necessario
enumerarli; è sufficiente sapere che qualche errore è sufficiente perché valga la pena eseguire il test.
I seguenti collegamenti forniscono informazioni sull'acquisizione delle proposte di test da differenti tipi di modelli
di errore. I primi due sono modelli di errore espliciti; l'ultimo utilizza dei modelli impliciti.
Questi modelli di errore possono essere applicati a differenti prodotti di lavoro. Ad esempio, il primo descrive a che
servono le espressioni Booleane. E' possibile rilevare tali espressioni nel codice, nelle condizioni di guardia, nei
grafici di stato, nei diagrammi di sequenza e nelle descrizioni del linguaggio naturale delle funzionalità del metodo
(come quelle presenti in un'API pubblicata).
Di tanto in tanto è anche utile disporre di linee guida per prodotti di lavoro specifici. Consultare Linee guida: proposte di test per il grafico di stato e per i diagrammi di flusso.
Un determinato elenco di proposte di test potrebbe contenere proposte di test provenienti da diversi modelli di errore
e tali modelli di errore potrebbero derivare da più prodotti.
Si supponga che è in corso la progettazione dei test per un metodo che ricerca una stringa in una raccolta sequenziale.
Esso può rispettare o ignorare le maiuscole/minuscole nella propria ricerca e restituisce l'indice della prima
corrispondenza trovata o -1 se non viene rilevata alcuna corrispondenza.
int Collection.find(String string,
Boolean ignoreCase);
Ecco alcune proposte di test per questo metodo:
-
trovata corrispondenza nella prima posizione
-
trovata corrispondenza nell'ultima posizione
-
nessuna corrispondenza rilevata
-
rilevate due o più corrispondenze nella raccolta
-
le maiuscole/minuscole vengono ignorate, ma non verrebbe stabilita alcuna corrispondenza se si rispettassero le
maiuscole/minuscole
-
le maiuscole/minuscole vengono rispettate; viene individuata una corrispondenza esatta
-
le maiuscole/minuscole vengono rispettate; una stringa, con cui sarebbe stata rilevata una corrispondenza se le
maiuscole/minuscole fossero state ignorate, viene saltata
Sarebbe semplice implementare questi sette test, uno per ogni proposta di test. Tuttavia, differenti proposte di test
possono essere combinate in un singolo test. Ad esempio, il seguente test soddisfa le proposte di test 2, 6 e 7:
Configurazione: raccolta inizializzata su ["dawn", "Dawn"]
Richiamo: collection.find("Dawn", false)
Risultato previsto: il valore di ritorno è 1 (sarebbe 0 se non fosse stato ignorato "dawn")
Se le proposte di test non sono specifiche, è più semplice combinarle tra di loro.
E' possibile soddisfare tutte le proposte di test in tre test. Perché tre test che soddisfano sette proposte di test
dovrebbero essere migliori di sette test separati?
-
Quando si crea un elevato numero di test semplici, è frequente creare il test N+1 copiando il test N e adattandolo
affinché soddisfi la nuova idea di test. Il risultato, specialmente in un software più complesso, è che il test N+1
verifica il programma quasi nello stesso modo del test N. Esso percorre approssimativamente lo stesso percorso
attraverso il codice.
Un numero minore di test, ognuno che soddisfa diverse proposte di test, non consente un approccio di "copia e
adattamento". Ogni test sarà in qualche modo differente rispetto all'ultimo, utilizzando il codice in maniere
differenti e seguendo percorsi differenti.
Perché ciò dovrebbe essere migliore? Se l'elenco di proposte di test fosse completo, con una proposta di test per
ogni errore nel programma, la modalità di scrittura dei test non avrebbe alcuna importanza. Ma nell'elenco mancano
sempre alcune proposte di test che potrebbero individuare i bug. Facendo in modo che ogni test esegua azioni
differenti dall'ultima, aggiungendo cioè una varietà apparentemente non necessaria, si aumenta la possibilità che
uno di tali test scovi un bug per un banale colpo di fortuna. Infatti, nei test più brevi e più complessi aumentano
le possibilità di soddisfare una proposta di test di cui non si percepiva neanche la necessità.
-
Talvolta, quando si creano test più complessi, nuove proposte di test vengono alla mente. Ciò succede meno
frequentemente con i test semplici, perché quasi tutte le azioni eseguite sono esattamente identiche a quelle
dell'ultimo test, il che intorpidisce la mente.
Tuttavia, esistono delle ragioni che giustificano la mancata creazione di test complessi.
-
Se ogni test soddisfa una singola proposta di test e il test per la proposta 2 ha esito negativo, è subito chiara
la causa più probabile: il programma non gestisce una corrispondenza nell'ultima posizione. Se un test soddisfa le
idee 2, 6 e 7, è più arduo isolare l'errore.
-
I test complessi sono più difficili da comprendere e da gestire. L'intento del test è meno ovvio.
-
I test complessi sono più difficili da creare. La costruzione di un test che soddisfa cinque proposte di test
spesso richiede più tempo rispetto alla costruzione di cinque test che soddisfano ognuno una proposta. Inoltre, è
più semplice commettere degli errori, pensando di soddisfare tutte e cinque le proposte mentre se ne soddisfano
solo quattro.
In pratica, occorre trovare un equilibrio ragionevole tra la complessità e la semplicità. Ad esempio, i primi test a
cui si sottopone il software (generalmente i test iniziali) dovrebbero essere facili, semplici da comprendere e gestire e
progettati per cogliere i problemi più ovvi. I test successivi dovrebbero essere più complessi, ma non al punto tale da
non essere gestibili.
Una volta terminata una serie di test, è bene controllarli rispetto agli errori caratteristici di progettazione test
discussi in Concetto: Verifica dello sviluppatore.
Un elenco di proposte di test è utile per analisi ed esami dei prodotti di lavoro della progettazione. Ad esempio,
considerare questa parte di un modello di progettazione che mostra l'associazione tra le classi Reparto e
Dipendente.
Figura 1: Associazione tra le classi Reparto e Dipendente
Le regole per la creazione delle proposte di test da tale modello impongono di considerare il caso in cui un reparto
contiene molti dipendenti. Esaminando una progettazione e chiedendosi "cosa succede se, a questo punto, il reparto
contiene molti dipendenti?", è possibile rilevare errori di progettazione o di analisi. Ad esempio, si potrebbe
realizzare che è possibile trasferire un solo dipendente alla volta tra reparti. Ciò potrebbe essere un problema se
l'azienda è incline a rapide riorganizzazioni in cui è necessario trasferire numerosi dipendenti.
Tali errori, ossia i casi in cui è stata trascurata una possibilità, vengono chiamati errori di omissione.
Proprio come è avvenuto con gli errori stessi, dal proprio impegno di verifica sono stati probabilmente omessi i test
che rilevano tali errori. Ad esempio, consultare [GLA81], [OST84], [BAS87], [MAR00] e altri studi che mostrano con quanta frequenza vengono commessi errori di
omissione che sfociano poi nella distribuzione.
Il ruolo della verifica nelle attività di progettazione viene discusso ulteriormente in Concetto: Test-first Design.
La tracciabilità è una questione di compromessi. Vale la pena
trattarla, considerato il relativo costo di gestione? Questa domanda verrà presa in considerazione durante l' Compito: Definizione delle esigenze di tracciabilità e di
valutazione.
Quando vale la pena utilizzare la tracciabilità, è consuetudine ricondurre i test ai prodotti di lavoro che li hanno
inspirati. Ad esempio, è possibile parlare di tracciabilità tra un'API e i relativi test. Se l'API cambia, è evidente
quale test si dovrà modificare. Se il codice (che implementa l'API) cambia, è chiaro quali test occorre eseguire. Se un
test risulta inspiegabile, è possibile individuare l'API che lo ha ispirato.
L'elenco di proposte di test aggiunge un altro livello di tracciabilità. E' possibile ricondurre un test alle proposte
di test che soddisfa e quindi al prodotto di lavoro originale.
|