Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Java JUnit Test: guida completa

Esempio, prima teorico e poi pratico, sulla fase di testing di un'applicazione grazie all'uso di JUnit 4.0.
Esempio, prima teorico e poi pratico, sulla fase di testing di un'applicazione grazie all'uso di JUnit 4.0.
Link copiato negli appunti

In tutti gli ambienti di sviluppo, qualsiasi sia il processo di produzione del codice adottato, uno degli aspetti considerati poco importanti è la fase di testing. Spesso sottovalutata per mancanza di tempo o per assenza di volontà da parte degli sviluppatori o semplicemente perchè considerata di scarso interesse. D'altra parte la fase di testing ha uno spiacevole inconveniente: viene subito prima della release. Quindi quando ormai le scadenze sono prossime (o, molto più spesso superate) e il committente è scalpitante per vedere all'opera il prodotto finito e funzionante.

Nell'articolo ci occuperemo di test, prima da un punto di vista teorico, poi con un esempio pratico di unit testing sviluppato con il tool sicuramente più utilizzato in ambiente Java: Junit (versione 4).

Test di unità e Test Driven Development

Una buona norma (best practice) che ogni sviluppatore dovrebbe effettuare è quello di eseguire test di unità per assicurarsi che la singola unità di sviluppo assolva le sue funzioni seguendo i requisiti. Questo è uno dei più importanti passaggi per poter avere un prodotto finale di buona qualità grazie ad un processo rapido di integrazione del software. Basta riflettere sull'idea di stratificazione del software per immaginare quanto sia complesso il processo di debug se qualcosa va storto nelle unità di base.

Il test di unità può essere sviluppato in maniera indipendente dallo sviluppatore, ma è buona norma avere un prodotto standard (se non altro a livello della stessa azienda) e aderire ad una pratica comune. Sicuramente la cosa più banale che possa esistere (ma non sottovalutatene l'importanza) è testare la singola classe con un main che valuti la bontà dei metodi. Questa pratica, seppure buona nelle intenzioni, ha delle evidenti limitazioni, come quella di non poter essere ripetuta nel tempo (ad esempio quando cambia un modulo del software) per i cosiddetti test di regressione (regression tests).

A tal proposito qualche anno fa (un bel po', oramai) Eric Gamma (uno dei famosi quattro dei Design Pattern) insieme a Kent Beck (creatore dell'Extreme Programming) iniziarono il progetto JUnit il cui scopo è quello di avere un framework per sviluppare test di unità secondo un semplice modello.

I cicli di sviluppo classici dell'ingegneria del software vanno quasi scomparendo soppiantati dall'utilizzo sempre più massiccio di metodologie agili (il cosiddetto Agile Programming, XP, ecc) il cui beneficio è quello di snellire le classiche fasi di analisi/sviluppo/test soprattutto in ambito Web based e in un contesto di mercato dinamicissimo.

Non entreremo nel merito di questi cicli di produzione poiché, sebbene legati al contesto, esulano dalla discussione, ma è interessante parlare della tecnica nota come Test Driven Development. Il TDD è un semplice modello di sviluppo che propone la scrittura dei test di unità prima ancora di sviluppare il codice. In questo modo, analizzando i requisiti e le esigenze del software si potrà giungere in una serie di iterazioni al prodotto finale.

In alcuni contesti ha sicuramente una sua utilità, ma probabilmente non se slegato da una buona fase di analisi dei requisiti e di disegno dell'applicazione.

JUnit 4: Annotation vs Reflection

Un test di unità è una semplice prova fatta per verificare che un requisito sia soddisfatto dal codice che abbiamo scritto. L'idea dello unit test in Java è quella di valutare ogni singolo metodo in funzione dei valori attesi.

Ipotizziamo la situazione in cui abbiamo creato una funzione matematica che converta un numero positivo in un numero negativo. Un caso di test (test case) verificherà che, quando passiamo il valore 5, il risultato sia -5. Se il test ha esito negativo evidentemente c'è un errore da correggere.

Abbiamo visto in teoria cosa siano i test di unità e la loro valenza in un ambiente di sviluppo. Iniziamo a prendere confidenza con JUnit, il tool prediletto da molti per l'automazione dei test di unità. A partire dalla versione 4, si avvale delle novità apportate da Java 5 (annotations) per rendere lo sviluppo di test suite davvero semplice ed immediato.

Nelle vecchie versioni creare uno unit test era comunque semplice ma bisognava conoscere la struttura della classe da estendere e per convenzione chiamare i metodi di test (i test case) testXXX in modo che potessero essere eseguiti automaticamente (per reflection). Con le annotazioni tutto ciò si può facilmente evitare in quanto annotiamo ogni singolo metodo che desideriamo faccia parte dei nostri test case e, a runtime, le annotazioni verranno valutate.

Il processo di esecuzione quindi è guidato dalle annotazioni che vediamo di seguito:

Annotazione Utilizzo
@Test per annotare i metodi di test
@Before per annotare un metodo da eseguire prima dell'esecuzione di un test case (ad esempio l'apertura di una connessione ad un database, o uno stream)
@After per annotare un metodo da eseguire dopo l'esecuzione di un test case (chiusura di risorse aperte in precedenza)
@BeforeClass come @Before
@AfterClass come @After
@Ignore utilizzata per evitare l'esecuzione di un test (evitando di commentare)

L'annotazione @Test

package com.buongiorno.junit;
//import static
import static org.junit.Assert.*;
import org.junit.*;
public class UnitTest {
@BeforeClass
public static void initClass() {
System.out.println("initClass()");
}
@AfterClass
public static void endClass() {
System.out.println("endClass()");
}
@Before
public void initMethod() {
System.out.println("initMethod()");
}
@After
public void endMethod() {
System.out.println("end Method");
}
..//

Nella prima parte vediamo i metodi annotati come @Before(Class) @After(Class)

La parte più interessante segue:

@Test
public void test1() {
System.out.println("Test 1");
assertTrue(true);
assertFalse(false);
}
@Test
public void test2() {
System.out.println("Test 2");
assertTrue(false);
}
@Test
public void test3() throws Exception {
System.out.println("Test 3");
throw new Exception();
}
@Test(timeout=100)
public void xyzTesting() throws InterruptedException {
System.out.println("Test xyzTesting");
//fallirà per timeout scaduto
Thread.sleep(200);
assertTrue(true);
}
@Test(expected=java.lang.Exception.class)
public void nuovoTest() throws Exception {
System.out.println("Test nuovoTest");
assertTrue(true);
throw new Exception();
}
}

I metodi test (annotati opportunamente dall'annotazione @Test org.junit.Assert assertTrue assertFalse

Un test potrebbe sollevare una eccezione. Quindi testare la robustezza di un'unità significa forzatamente sollevare un'eccezione. In questi casi è interessante l'uso del parametro "expected" nell'annotazione (nel codice è il metodo nuovoTest() java.lang.Exception

Altro interessante parametro (metodo xyzTesting() sleep()

Come abbiamo visto, definire dei test di unità è molto facile. Per eseguire il test (e per compilarlo) avete bisogno della libreria di Junit che trovate disponibile al download al sito www.junit.org. La classe main di Junit si aspetta come parametro la classe di test (quella che abbiamo scritto). Se sviluppate con Eclipse, avrete direttamente a disposizione questa libreria ed un plugin con cui potrete effettuare direttamente i test dalla console di Eclipse con una visualizzazione grafica dei test passati e dei test falliti.

Ti consigliamo anche