Confronto Tra JUnit 3 e JUnit 4
Short Description
Download Confronto Tra JUnit 3 e JUnit 4...
Description
Confronto tra Junit 3 e Junit 4 Metodi di test Tutte le versioni precedenti di JUnit utilizzano convenzioni di codifica e riflessione per individuare i test. Per esempio, il codice seguente test verifica che 1 + 1 sia uguale a 2: 1. import junit.framework.TestCase; 2. 3. public class AdditionTest extends TestCase { 4. 5. private int x = 1; 6. private int y = 1; 7. 8. public void testAddition() { 9. int z = x + y; 10. assertEquals(2, z); 11. } 12. } Al contrario in JUnit 4 i test sono indicati da una annotazione @Test, come illustrato di seguito: 1.import org.junit.Test; 2.import junit.framework.TestCase; 3. 4.public class AdditionTest extends TestCase { 5. 6. private int x = 1; 7. private int y = 1; 8. 9. @Test public void testAddition() { 10. int z = x + y; 11. assertEquals(2, z); 12. } 13.} In questo modo non è più necessario chiamare i metodi testQualcosa1(), testQualcosa2(), e così via. Per esempio, anche il seguente approccio funziona egregiamente: 1.import org.junit.Test; 2.import junit.framework.TestCase; 3. 4.public class AdditionTest extends TestCase { 5. 6. private int x = 1; 7. private int y = 1; 8. 9. @Test public void additionTest() {
10. int z = x + y; 11. assertEquals(2, z); 12. } 13.} Come anche questo… 1. import org.junit.Test; 2. import junit.framework.TestCase; 3. 4. public class AdditionTest extends TestCase { 5. 6. private int x = 1; 7. private int y = 1; 8. 9. @Test public void addition() { 10. int z = x + y; 11. assertEquals(2, z); 12. } 13.} Questo permette di seguire la convenzione di codifica che meglio si adatta alle proprie applicazioni e al proprio stile di programmazione. Per esempio, alcuni programmatori adottano una convenzione in cui la classe di test usa gli stessi nomi dei metodi della classe da testare: List.contains() viene testato da ListTest.contains(), List.addAll() viene testato da ListTest.addAll(), e così via. La classe TestCase è ancora presente, ma non è più necessario estenderla. Finché si annotano i metodi di test con @Test, si possono inserire metodi di test in qualsiasi classe. È però necessario importare la classe junit.Assert per accedere ai vari metodi assert, come nel codice seguente: 1. import org.junit.Assert; 2. 3. public class AdditionTest { 4. 5. private int x = 1; 6. private int y = 1; 7. 8. @Test public void addition() { 9. int z = x + y; 10. Assert.assertEquals(2, z); 11. } 12.} È anche possibile usare la nuova funzionalità di importazione static del JDK 5 per rendere tutto ciò più semplice rispetto alla vecchia versione di JUnit: 1. import static org.junit.Assert.assertEquals; 2. 3. public class AdditionTest { 4. 5. private int x = 1;
6. private int y = 1; 7. 8. @Test public void addition() { 9. int z = x + y; 10. assertEquals(2, z); 11. } 12.} Questo approccio rende il testing di metodi protetti assai più facile, perché la classe di test può ora estendere la classe che contiene i metodi protetti. I metodi SetUp e TearDown Gli strumenti di esecuzione dei test di JUnit 3 eseguono automaticamente il metodo setUp() prima di eseguire ciascun metodo di test. Di norma, si usa setUp() per inizializzare i dati necessari per l’esecuzione dei test, reimpostare eventuali variabili d’ambiente, e così via. Per esempio, ecco un metodo setUp(): 1. protected void setUp() { 2. 3. System.setErr(new PrintStream(new ByteArrayOutputStream())); 4. 5. inputDir = new File("data"); 6. inputDir = new File(inputDir, "xslt"); 7. inputDir = new File(inputDir, "input"); 8. 9. } Anche in JUnit 4 è possibile inizializzare i dati e configurare l’ambiente prima dell’esecuzione di ogni metodo di test: però il metodo che esegue tali operazioni non deve necessariamente chiamarsi setUp(). Deve solo essere marcato con l’annotazione @Before: 1. @Before protected void initialize() { 2. 3. System.setErr(new PrintStream(new ByteArrayOutputStream())); 4. 5. inputDir = new File("data"); 6. inputDir = new File(inputDir, "xslt"); 7. inputDir = new File(inputDir, "input"); 8. } È altresì possibile avere diversi metodi annotati con @Before: in tal caso, ciascuno di essi sarà eseguito prima di ogni test: 1. @Before protected void findTestDataDirectory() { 2. inputDir = new File("data"); 3. inputDir = new File(inputDir, "xslt"); 4. inputDir = new File(inputDir, "input"); 5. } 6. @Before protected void redirectStderr() { 7. System.setErr(new PrintStream(new ByteArrayOutputStream()));
8. } Anche le operazioni di cleanup operano in modo analogo. In JUnit 3, si usa il metodo tearDown(): si noti, in questo frammento di codice, la chiamata alla Garbage Collection per forzare il recupero di memoria. 1. 2. 3. 4.
protected void tearDown() { doc = null; System.gc(); }
Con JUnit 4, è sufficiente annotare un metodo qualsiasi con @After: 1. 2. 3. 4.
@After protected void disposeDocument() { doc = null; System.gc(); }
Come con l’annotazione @Before, si possono creare diversi metodi di cleanup annotati con @After, ciascuno dei quali sarà eseguito dopo ogni test. Inoltre, non è più necessario chiamare esplicitamente i metodi di inizializzazione e di cleanup della superclasse. Fino a quando non vengono sovrascritti, il “test runner” chiamerà questi metodi automaticamente, se necessario. I metodi @Before presenti nelle superclassi sono invocati prima prima dei metodi @Before delle sottoclassi (si noti come questo comportamento rispecchi l’ordine di chiamata dei costruttori). I metodi @After sono eseguiti in senso inverso: prima quelli delle sottoclassi e poi quelli delle superclassi. Inizializzazione delle classi di test JUnit 4 introduce anche una nuova funzionalità che non ha equivalenti in JUnit 3: un metodo di inizializzazione, simile a setUp(), che opera a livello di classe. Ogni metodo annotato @BeforeClass sarà eseguito una volta, subito dopo il caricamento della classe di test, e prima che i metodi di test siano eseguiti; di contro, ogni metodo annotato con @AfterClass verrà eseguito una sola volta, dopo che tutti i metodi di test sono stati eseguiti. Per esempio, si supponga che ciascuna classe di test usi una connessione a un database, una connessione di rete, una grande struttura dati, o qualche altra risorsa che è particolarmente oneroso inizializzare o scaricare. Invece di ricreare queste risorse prima di ogni test, è possibile inizializzarle e deinizializzarle una sola volta. Questo approccio renderà l’esecuzione di alcuni casi di test assai più rapida. 1. private PrintStream systemErr; 2. 3. @BeforeClass protected void redirectStderr() { 4. systemErr = System.err; // Hold on to the original value 5. System.setErr(new PrintStream(new ByteArrayOutputStream())); 6. } 7. 8. @AfterClass protected void tearDown() { 9. // restore the original value 10. System.setErr(systemErr);
11.} Come testare le eccezioni Il meccanismo di test delle eccezioni è uno dei principali miglioramenti apportato a JUnit 4. Nelle vecchie versioni di Junit si usava un blocco try contenente il codice che dovrebbe generare l’eccezione, e una chiamata a fail() dalla fine del blocco try. Per esempio, il codice seguente verifica che sia generata una ArithmeticException: 1. public void testDivisionByZero() { 2. 3. try { 4. int n = 2 / 0; 5. fail("Divisione per zero."); 6. } 7. catch (ArithmeticException success) { 8. assertNotNull(success.getMessage()); 9. } 10.} In JUnit 4, è possibile scrivere esplicitamente il codice che genera l’eccezione: un’annotazione dichiara che è previsto che sia sollevata un’eccezione: 1. @Test(expected=ArithmeticException.class) 2. public void divideByZero() { 3. int n = 2 / 0; 4. } Se l’eccezione non viene sollevata o se è diversa da quella prevista, il test avrà esito negativo. Si tenga presente che potrebbe ancora essere utile il blocco try-catch vecchio stile, per esempio se si desidera verificare il messaggio d’errore, eventuali dettagli dell’eccezione o altre proprietà. Impostazione di tempi limite La verifica delle prestazioni è una delle aree più spinose nel mondo dei test di unità. JUnit 4 non risolve completamente il problema, ma offre un utile supporto: infatti, i metodi di test possono essere annotati con il parametro timeout. Se il test viene eseguito in un tempo superiore a quello indicato, fallisce. Per esempio, nel codice seguente il test non riesce se il metodo richiede più di 500 millisecondi per essere eseguito: 1. @Test(timeout=500) public void retrieveAllElementsInDocument() { 2. doc.query("//*"); 3. } Oltre al semplice benchmarking, i test cronometrati sono utili anche per i test su operazioni di rete. Se un host remoto o un database di test a cui si sta tentando di connettersi sono lenti o irraggiungibili, è possibile bypassare il test in modo da non rallentare i test seguenti: 1. @Test(timeout=2000) 2. public void remoteBaseRelativeResolutionWithDirectory()
3. throws IOException, ParsingException { 4. builder.build("http://www.qualcosa.org/xml"); 5. } Test ignorati L’annotazione @Ignore consente di marcare i metodi di test che per qualche ragione si desidera saltare. Si supponga di avere un metodo di test che richiede un tempo molto elevato per la sua esecuzione: non necessariamente si deve tentare di renderlo più veloce; può essere che il suo lavoro sia semplicemente assai complessa o naturalmente lento. I test che tentano di accedere a server remoti si trovano spesso in questa categoria. Per non rallentare gli altri metodi, è possibile annotare (anche temporaneamente) questo test in modo che sia ignorato. 1. @Ignore public void testUTF32BE() 2. throws ParsingException, IOException, XIncludeException { 3. 4. File input = new File( 5. "data/xinclude/input/UTF32BE.xml" 6. ); 7. Document doc = builder.build(input); 8. Document result = XIncluder.resolve(doc); 9. Document expectedResult = builder.build( 10. new File(outputDir, "UTF32BE.xml") 11. ); 12. assertEquals(expectedResult, result); 13.} Durante l’esecuzione della classe di test, questo metodo sarà ignorato. Nuove asserzioni JUnit 4 aggiunge due nuovi metodi assert() per il confronto di array; di seguito le definizioni: 1. public static void assertArrayEquals(Object[] expecteds, Object[] actuals) 2. public static void assertArrayEquals(String message, Object[] expecteds, 3. Object[] actuals) Questi due metodi confrontano gli array nel modo più ovvio: due array sono uguali se sono della stessa dimensione e se ciascun elemento è uguale a quello corrispondente nell’altro array.
View more...
Comments