|
JUnit
Continuiamo l'esempio iniziato con l'articolo del mese
scorso [3].
Si trattava volutamente di un esempio numerico, per
mostrare che anche in casi come questi, spesso utilizzati
nei corsi di informatica di base, l'automazione del
test può essere esercitata e rappresenta un valore
aggiunto.
Riprendo la descrizione del problema.
Si trattava di definire in una classe, un metodo (chiamato
poi modifica()), che ricevuto un intero in input restituisse
la metà del suo argomento se questi è
pari o il triplo più uno se invece è dispari.
Il metodo andava in seguito usato per stabilire (altro
metodo, chiamato sequenzaSingola()) quanto è
lunga la sequenza di applicazioni ripetute della funzione
fino ad ottenere 1 per un valore qualsiasi positivo
maggiore di 2 (valore: variabile di istanza inizializzata
dal costruttore della classe). Per applicazione ripetuta
si intende che il valore restituito da una chiamata
viene passato come argomento ad una chiamata successiva.
La lunghezza della sequenza trovata è da registrare
nell'oggetto istanziato, da cui, inoltre, deve sempre
essere possibile, attraverso un metodo, richiamarne
il valore (int getLength()) e il numero che ha generato
una sequenza di questa lunghezza (int getValue()).
Esempio
di sequenza:
valore
iniziale: 10
lunghezza della sequenza: 6 (10 5 16 8 4 2)
La funzione sequenzaSingola() così come implementata
e verificata nell'articolo precedente, non controlla
il dominio del valore di cui deve calcolare la lunghezza
della sequenza.
static
int sequenzaSingola(int start){
int val = start, count = 0;
while(val>1){
val=modifica(val);
count++;
}
return
count;
}
Eccezioni
Se torniamo alla definizione del problema, ci accorgiamo
che il valore di cui cerchiamo la lunghezza della sequenza
dev'essere positivo e maggiore di 1.
Questo significa che nell'implementazione della soluzione
dobbiamo prevedere una verifica e un'azione da eseguire
nel caso in cui il controllo desse risultato negativo.
L'azione scelta in questo caso è il lancio di
un'eccezione.
static
int sequenzaSingola(int start) throws Exception{
if
(start<2)
throw
new Exception("Valore fuori dominio");
int
val = start, count = 0;
while(val>1){
val=modifica(val);
count++;
}
return
count;
}
A
questo punto prima adattiamo il metodo di test esistente,
poi ne aggiungiamo un secondo:
public
void testSequenzaSingola(){
try{
assertTrue(Sequenza.sequenzaSingola(10)
== 6);
assertTrue(Sequenza.sequenzaSingola(12)
== 9);
}
catch(Exception
e){
assertTrue(false);
}
}
public
void testSequenzaSingolaNegativa(){
try{
Sequenza.sequenzaSingola(-2);
assertTrue(false);
}
catch(Exception
e){
assertTrue(true);
}
}
Quest'ultimo esempio mostra come gestire le eccezioni
all'interno di metodi di test in JUnit. Il primo caso
prevede un assert negativo all'interno del blocco catch,
dove il controllo del programma non dovrebbe mai entrare,
mentre il secondo prevede un assert negativo come istruzione
che segue la chiamata che dovrebbe lanciare l'eccezione.
Questo ci assicura il controllo che il metodo non termina
regolarmente.
Ultimi
elementi
Introduciamo
ora gli ultimi elementi di soluzione del problema, osservando
la classe intera:
public
class Sequenza{
int
valore, contatore;
public
Sequenza(int v){
valore
= v;
contatore
= 0;
}
static
int modifica(int val){
...
}
static
int sequenzaSingola(int start) throws Exception{
...
}
public
int getValue(){
return
valore;
}
public
int getLength() throws Exception{
if
(contatore == 0){
contatore
= sequenzaSingola(valore);
return
contatore;
}
else
return
contatore;
}
Ed ecco il metodo che verifica getValue() e getLength():
public
void testLength(){
Sequenza
seq = new Sequenza(10);
assertTrue(seq!=null);
assertTrue(seq.getValue()
== 10);
try{
assertTrue(seq.getLength()
== 6);
}
catch(Exception
e){
assertTrue(false);
}
}
Web Test
Un
tipo di programma che necessita senz'altro di test è
quello rappresentato dalle applicazioni Web. Non tanto
test di unità (questi andrebbero integrati nel
codice a livello server, ad esempio con JUnit se il
server utilizza Java), ma piuttosto test di funzionalità,
che permettano di scoprire se un link non esiste più,
se la risposta ad una form è diversa oggi da
ieri, ecc.
Questi test sono necessari per le caratteristiche intrinseche
di queste applicazioni, che cambiano velocemente nel
tempo, sia in aspetto che in funzionalità.
Il metodo più diretto per verificare la funzionalità
di siti Web è quello di verificare il comportamento
del client, sottoposto a sessioni di interazione con
il server. Anche in questo caso esistono prodotti disponibili
in rete. Vediamone due a tale scopo, entrambi con licenza
open source, HttpUnit (http://httpunit.sourceforge.net)
e Canoo WebTest (http://webtest.canoo.com), mostrando
due semplici esempi di utilizzo.
HttpUnit
Malgrado il nome tragga in inganno, si tratta di una
API di classi Java per test funzionali, non test di
unità. Viene utilizzato per verificare nel tempo
la consistenza di siti Web.
La classe principale è WebConversation, che rappresenta
un browser che conversa con un server. Attraverso questa
classe è possibile costruire una serie di interazioni
con il server.
Ecco l'esempio mostrato nel cookbook:
WebConversation
wc = new WebConversation();
WebResponse resp =
wc.getResponse("http://httpunit.sourceforge.net/doc/Cookbook.html");
WebLink link = resp.getLinkWith("response");
WebRequest req = link.getRequest(); WebResponse jdoc
= wc.getResponse(req);
L'oggetto di tipo WebResponse rappresenta la pagina
ottenuta all'indirizzo specificato. All'interno di questa
pagina sono possibili varie operazioni come getText(),
getTitle(), getTables(), getLinks(), ecc. Tutte queste
operazioni si riferiscono al testo ottenuto.
Nell'esempio specifico si cerca il link che corrisponda
al testo "response". Il link viene rappresentato
dall'oggetto WebLink, dal quale è poi possibile
partire con ulteriori richieste, come appunto getRequest(),
utilizzata nell'esempio.
Canoo
WebTest
Canoo
WebTest (http://webtest.canoo.com) è simile a
HttpUnit come funzionalità di base, ma compie
un passo ulteriore permettendo di specificare le sequenze
di test in XML.
Può sembrare una differenza irrilevante, anzi
inutile, per programmatori Java. In realtà questo
livello in più è quello che permette anche
a non sviluppatori Java di creare test e soprattutto
di aggiornarli. Infatti la differenza tra test di unità
e test funzionali consiste anche in questo, cioè
nel fatto che i test funzionali possono essere creati
anche da persone non direttmente coinvolte nello sviluppo.
Chi partecipa alla mailing list comunitaria di Canoo
WebTest riporta che in alcuni casi questo tool è
stato utilizzato in un primo passaggio come definizione
di requisiti, per poi, senza modifiche, passare a test
vero e proprio.
Il motore di Canoo WebTest è Ant [4], un prodotto
del progetto Jakarta [5] di Apache.
Ecco l'esempio di utilizzo riportato nel whitepaper:
<target
name="login">
<testSpec name="normal" >
<config
host="www.xyz.com"
port="80"
protocol="http"/>
<steps>
<invoke stepid="get Login Page" url="login.jsp"/>
<verifytitle stepid="we should see the login
title"
text="Login Page" />
<setinputfield stepid="set user name"
name="username"
value="scott" />
<setinputfield stepid="set password"
name="password"
value="tiger" />
<clickbutton stepid="Click the submit button"
label="let me in" />
<verifytitle stepid="Home Page follows if login
ok"
text="Home Page" />
</steps>
</testSpec>
</target>
Ogni
specificazione di test consiste in una configurazione
(<config>, che è anche possibile specificare
in un file separato condivisibile tra più test)
e una sequenza di passi, che simula il comportamento
di un potenziale client.
Come si vede si possono verificare elementi all'interno
della pagina chiamata, attivare pulsanti, riempire campi
di testo, ecc.
Una cosa importante da notare è che Canoo WebTest,
come del resto HttpUnit, si basa sull'interazione del
client con il server: è perciò indipendente
dal linguaggio usato per l'applicazione server. Può
quindi essere applicato a qualsiasi sito Web.
Si tratta di un prodotto open source alla cui estensione
hanno contribuito già in parecchi e tutti possono
continuare a farlo. La cosa è resa semplice dal
fatto che ogni nuovo tag XML può essere implementato
in modo completamente indipendente dal resto dell'applicazione.
Conclusioni
In questi due articoli abbiamo cercato di mostrare come
il test automatico (sia di unità che funzionale)
debba essere considerato parte integrante della progettazione
e dello sviluppo di applicazioni. Questo non viene ancora
insegnato in modo sistematico, ma i tools ottenibili
sulla rete stanno contribuendo sempre più a diffondere
questa attività anche tra i programmatori Java.
Bibliografia
[1]
HttpUnit: http://httpunit.sourceforge.net
[2] Canoo WebTest: http://webtest.canoo.com
[3] Pedrazzini Sandro: Con il test in testa: Parte I,
Moka Byte, http://www.mokabyte.com, Maggio 2002.
[4] Ant, the leading build automation tool: http://jakarta.apache.org/ant
[5] The Jakarta Project: http://jakarta.apache.org
|