|
Vediamo un esempio di una TestSuite in JUnit. Ogni test è composto da test case individuali.Ogni test case è una classe che estende TestClass e contiene ad esempio il codice di test che è stato usato nel main() nel listato 1. Per convenzione la classe di test consiste dello stesso nome della classe da testare, ma con Test all'inizio (ad es. TestHelloWorld.java) e si trova all'interno dello stesso package della classe con l'aggiunta di test (ad es. se abbiamo hello.TestHelloWorld allora il package della classe di test sarà hello.test.TestHelloWorld). Il metodo sayHello() della classe HelloWorld deve essere modificato in modo da restituire la stringa "Hello World", il comportamento della classe rimane lo stesso perché all'interno del main() stampiamo il valore restituito da sayHello(). Listato 2: /** * Classe di test, stampa "Hello World" * * @autor Ettore Marcon * @version 0.2 */ class HelloWorld { /** * Restituisce "Hello World" * * @return La stringa "Hello World" */ public String sayHello(){ return "Hello World"; } /** * Metodo main con il test della classe * * @param args Argomenti iniziali */ public static void main(String[] args){ HelloWorld world=new HelloWorld(); System.out.println(world.sayHello()); } }// fine classe La classe di test estende TestCase e al suo interno tutti i metodi di test inziano con testXXX. Il metodo testSayHello() confronta la stringa "Hello World" con quella che viene restituita dalla funzione sayHello() se non coincidono il test fallisce. Listato 3: package hello.test; import hello.HelloWorld; import junit.framework.TestCase; import junit.framework.AssertionFailedError; /** * Classe di test JUnit contenente i test case per HelloWorld * * @autor Ettore Marcon * @version 0.1 */ public class TestHelloWorld extends TestCase{ /** * Costruttore di default * * @param name Nome della classe */ public TestHelloWorld(String name){ super(name); } /** * Metodo main per il test della classe HelloWorld * * */ public static void main(String[] args){ junit.textui.TestRunner.run(TestHelloWorld.class); } /** * Test per il controllo del valore restituito dal metodo sayHello() */ public void testSayHello(){ HelloWorld world = new HelloWorld(); assertEquals("Hello World",world.sayHello()); }// fine test }// fine classe Supponiamo in fase di manutenzione di cambiare la classe HelloWorld e rendere "Hello World" una stringa statica. Durante la digitazione per errore possiamo scrivere "Hell0 World" al posto della 'o' scrivere uno '0'. Listato 4: package hello; /** * Classe di test, stampa "Hello World" * * @autor Ettore Marcon * @version 0.3 */ public class HelloWorld { private final static String HELLO_WORLD="Hell0 World"; /** * Restituisce "Hello World" * * @return La stringa "Hello World" */ public String sayHello(){ return HELLO_WORLD; } }// fine classe All'esecuzione di TestHelloWorld otteniamo questo messaggio d'errore: .F Time: 0,01 There was 1 failure: 1) testSayHello(hello.test.TestHelloWorld)junit.framework.AssertionFailedError: expected:<Hello World> but was:<Hell0 World> at hello.test.TestHelloWorld.testSayHello(TestHelloWorld.java:41) at hello.test.TestHelloWorld.main(TestHelloWorld.java:31) FAILURES!!! Tests run: 1, Failures: 1, Errors: 0 Questo errore è difficile da scovare nel codice, ma se vengono creati dei test per ogni metodo o classe in seguito ad una modifica possiamo controllare se il comportamento della classe è ancora quello che ci si aspetta. Vediamo adesso un esempio un po' più complesso, supponiamo di dover controllare il funzionamento di un metodo retriveQuestionMulti() della classe ForEachQuestion. Il metodo restituisce un hashtable con chiave e valore scomponendo due stringhe, la prima rappresenta le chiavi e la seconda i valori. import java.util.*; /** * ForEach per processare le domande di una lista * * @author Ettore Marcon * @version $Revision: 1.6 $ */ public class ForEachQuestion{ /** class version */ public static final String CLASS_VERSION = "$Id: ForEachQuestion.java,v 1.6 2002/07/01 14:34:54 emarcon Exp $"; /** separatore */ public static final char CHAR_SEPARATOR=','; /** * Costruttore di default */ public ForEachQuestion(){ } /** * Gestione risposte multiple con ad es. question=1,3,5 e subQuestion= * * @param question Numero domanda * @param multi Numero di risposte multiple alla domanda * @return Ritorna un hashtable con chiave il numero di domanda e valore il numero totale di risposte */ public Hashtable retriveQuestionMulti(String question, String multi){ // controllo liste non vuote if (question==null || multi==null || question.length()==0 || multi.length()==0){ return null; }// end if Hashtable list=new Hashtable(); int i=0,iprec=0,j=0,jprec=0; // valori iniziali i=question.indexOf(CHAR_SEPARATOR,iprec); j=multi.indexOf(CHAR_SEPARATOR,jprec); while (i!=-1 && j!=-1){ list.put(question.substring(iprec,i++),multi.substring(jprec,j++)); iprec=i; jprec=j; i=question.indexOf(CHAR_SEPARATOR,iprec); j=multi.indexOf(CHAR_SEPARATOR,jprec); }// end if list.put(question.substring(iprec,(i!=-1)?i:question.length()),multi.substring(jprec, (j!=-1)?j:multi.length())); return list; }// end method }// end class Il metodo è molto più complesso da controllare rispetto alla classe SayHello, infatti in questo caso dobbiamo considerare diversi casi. La classe di test che è stata realizzata è la seguente: import ForEachQuestion; import java.util.*; import junit.framework.*; import junit.extensions.*; /** * JUnit testcases for ForEachQuestion * * @author Ettore Marcon * @version $Id: TestForEachQuestion.java,v 1.2 2002/06/21 13:52:28 emarcon Exp $ */ public class TestForEachQuestion extends TestCase{ /** * Costruttore di default */ public TestForEachQuestion(String name) { super(name); } /** * Main, e' possibile il test diretto * * @param args[] Argomenti */ public static void main(String args[]) { junit.textui.TestRunner.run(TestForEachQuestion.class); } // Classe oggetto di test private ForEachQuestion test; // Risultato dei test Hashtable result; /** * Imposta i dati necessari ad ogni test */ public void setUp(){ test=new ForEachQuestion(); result=null; } /** * Rimuove i dati */ public void tearDown(){ test=null; } /** * Lista question null multi null */ public void testQuestionNullMultiNull(){ result=test.retriveQuestionMulti(null,null); assertEquals("Hash ritornata dovrebbe essere null",null,result); } /** * Lista question null */ public void testQuestionNull(){ result=test.retriveQuestionMulti(null,"2"); assertEquals("Hash ritornata dovrebbe essere null",null,result); } /** * Lista multi null */ public void testMultiNull(){ result=test.retriveQuestionMulti("2",null); assertEquals("Hash ritornata dovrebbe essere null",null,result); } /** * Lista question un valore ma niente multi */ public void testQuestionOne(){ result=test.retriveQuestionMulti("2",""); assertEquals("Hash ritornata dovrebbe essere null",null,result); } /** * Lista niente question ma niente multi */ public void testMultiOne(){ result=test.retriveQuestionMulti("","2"); assertEquals("Hash ritornata dovrebbe essere null",null,result); } /** * Lista question 1 e multi 2 */ public void testQuestion1Multi2(){ result=test.retriveQuestionMulti("1","2"); assertEquals("Hash ritornata dovrebbe essere key 1 e value 2","2",(String)result.get("1")); } /** * Lista question 1 e multi 2,3 */ public void testQuestion1Multi23(){ result=test.retriveQuestionMulti("1","2,3"); assertEquals("Hash ritornata dovrebbe essere key 1 e value 2","2",(String)result.get("1")); } /** * Lista question 1,3 e multi 2,3 */ public void testQuestion13Multi2(){ result=test.retriveQuestionMulti("1,3","2"); assertEquals("Hash ritornata dovrebbe essere key 1 e value 2","2",(String)result.get("1")); } /** * Lista question con virgola */ public void testQuestionVirgolaMulti2(){ result=test.retriveQuestionMulti(",","2"); assertEquals("Hash ritornata dovrebbe essere key 1 e value 2","2",(String)result.get("")); } }// end class I test proposti sono abbastanza chiari grazie all'uso di commenti all'interno delle condizioni di assert. Nei test è importante considerare tutte le varie possibilità. I casi particolari sono quelli da valutare attentamente. Se durante lo sviluppo o dopo il rilascio si dovesse trovare un bug all'interno della classe si dovrà costruire un unit test che simuli il bug e la classe verrà nuovamente rilasciata solo se supererà tutti i test della suite.
|