Vai alla Home Page About me Courseware Federica Living Library Federica Federica Podstudio Virtual Campus 3D Le Miniguide all'orientamento Gli eBook di Federica La Corte in Rete
 
I corsi di Scienze Matematiche Fisiche e Naturali
 
Il Corso Le lezioni del Corso La Cattedra
 
Materiali di approfondimento Risorse Web Il Podcast di questa lezione

Sergio Di Martino » 14.Lo Scaffolding - JUnit


Obiettivi della lezione

  • Comprendere i concetti di verifica dinamica
  • Scaffolding
  • JUnit

Testing di Unità

Il testing è applicato isolatamente ad una unità (modulo) di un sistema software.
Obiettivo fondamentale è quello di rilevare errori (logica e dati) nel modulo.
Motivazioni:

  • si riduce la complessità concentrandosi su una sola unit;
  • è più facile correggere i bug, poiché poche componenti sono coinvolte.

Unità: elemento definito nel progetto di un sistema software e testabile separatamente unità e modulo sono spesso usati come sinonimi.

Test stub e test driver

Per eseguire un test case su una unità (o una combinazione di unità) è necessario che questa sia isolata dal resto del sistema.

E’ quindi necessario scrivere del codice aggiuntivo per permettere ad un’unità di eseguire in isolamento.

Test driver e test stub sono usati per sostituire le parti mancanti del sistema.
Un test driver (o Modulo Guida) è un blocco di codice che inizializza e chiama la componente da testare.

  • Deve simulare l’ambiente chiamante.
  • Deve occuparsi dell’inizializzazione dell’ambiente non locale del modulo in esame.

Un test stub (o Modulo Fittizio) è una implementazione parziale di componenti da cui la componente testata dipende (componenti che sono chiamate dalla componente da testare).

Espone le stesse signatures dei moduli simulati, ma è più semplice

L’insieme del Test driver e di tutti i test stub forma lo Scaffolding, cioè l’ambiente per eseguire test case su un’unità isolata.

Implementazione di Test stub

Intuitivamente, ogni qual volta una unità da testare contiene un riferimento ad un’altra classe/modulo, questa va “simulata”, al fine di fare test in isolamento.

La simulazione è effettuata attraverso la scrittura di un codice fittizio, contenuta in un test stub.

Un test stub deve fornire la stessa API del metodo della componente simulata e ritornare un valore il cui tipo è conforme con il tipo del valore di ritorno specificato nella signature.

Se l’interfaccia di una componente cambia, anche il corrispondente test stub deve cambiare.

L’implementazione di un test stub non è una cosa semplice.

  • Non è sufficiente scrivere un test stub che semplicemente stampa un messaggio attestante che il test stub è in esecuzione.
  • La componente chiamata deve fare un qualche lavoro.
  • Il test stub non può restituire sempre lo stesso valore.

Il problema dello scaffolding

Creare l’ambiente per l’esecuzione dei test è un’attività non banale.

Lo scaffolding è estremamente importante per il test di unità e integrazione.

Può richiedere un grosso sforzo programmativo.

Uno scaffolding buono è un passo importante per test di regressione efficiente.

La generazione di scaffolding può essere parzialmente automatizzato a partire dalle specifiche …

Esistono pacchetti software per generare scaffolding: JUnit, NUnit, PUnit, etc…

 

Lo Scaffolding.

Lo Scaffolding.


JUnit

Junit è un framework di unit testing per Java.
Autori: Erich Gamma, Kent Beck.

Cosa è un JUnit Test?
Un test “script” è un insieme di metodi Java.

  • JUnit offre principalmente funzionalità di Test Driver.
  • L’ idea è quella di istanziare una classe Java (l’unità da testare), eseguire dei metodi e determinare se la computazione è corretta.

Cosa offre JUnit? Gli Assert.

  • Un package di metodi che verifica la presenza di diverse proprietà:
    • “uguaglianza” di oggetti;
    • riferimenti agli oggetti identici;
    • riferimenti agli oggetti null / non-null.
  • Le asserzioni sono utilizzate per determinare l’esito del test case.

Esito del test case

Un esito è il risultato dell’esecuzione di un singolo test.

Successo (rappresentato con colore verde): il test case è andato a buon fine, e il software testato funziona come previsto

Fallito (rappresentato con colore rosso): il test case è andato a buon fine, ma il software testato non fuziona come previsto

Errore (rappresentato con colore blu): il test case non è andato a buon fine.

Le cause possono essere:

  • è avvenuto un evento inaspettato durante il test case;
  • il test case non è impostato correttamente.

Cosa fa Junit?

Il codice di un test basato su JUnit permette di fare:

  1. il setup di tutte le condizioni necessarie per il testing (creare gli oggetti necessari, allocare le risorse ecc);
  2. chiamare un metodo che deve essere testato;
  3. verificare che il metodo testato computi nella maniera attesa dall’oracolo;
  4. eventualmente pulire.

Assert

JUnit offre una serie di metodi per determinare se un metodo è eseguito correttamente o meno.

Questi sono i metodi assert, che permettono di confrontare il risultato della computazione di un metodo da testare con l’output predetto dall’oracolo.

Gli assert sono il cuore del framework Junit

Gli assert sono definiti nella classe Junit Assert.

  • Se l’assert è vero, il metodo continua l’esecuzione.
  • Se vi è un assert falso, il metodo ferma l’esecuzione in quel punto, e il risultato del test case sarà “Fallito”.
  • Se una qualunque altra eccezione viene generata durante l’esecuzione del metodo, il risultato per il test case sarà “Errore”.
  • Se nessun assert sarà violato durante l’esecuzione del metodo, il risultato per il test case sarà “Successo”.

Tutti i metodi assert sono statici.

I metodi di assert

Le condizioni booleane sono vere o false

assertTrue(condition)
assertFalse(condition)

Gli oggetti sono null o non-null

assertNull(object)
assertNotNull(object)

Gli oggetti sono identici (i.e. due riferimenti allo stesso oggetto), o non identici.

assertSame(expected, actual)
Vero se : expected == actual
assertNotSame(expected, actual)

 

I metodi di assert (segue)

“Uguaglianza” di oggetti:

assertEquals(expected, actual)
valido se: expected.equals( actual )

“Uguaglianza” di array:

assertArrayEquals(expected, actual)
gli array hanno la stessa lunghezza
per ogni valore valido di i, controlla a seconda dei casi:

assertEquals(expected[i],actual[i])
oppure
assertArrayEquals(expected[i],actual[i])

Vi è anche un assert di fallimento incondizionato fail(), che restituisce sempre un esito di fallimento.

Un test case JUnit 4


Un test case JUnit 4


Un test case JUnit 4


Un test case JUnit 4


Assert di uguaglianza

assertEquals(a,b) si basa sul metodo equals() della classe testata.

  • L’effetto è quello di valutare a.equals( b ).
  • Spetta alla classe testata stabilire una relazione di uguagianza adeguata. JUnit utilizza ciò che è disponibile.
  • Tutte le classi testate che non sovrascrivono il metodo equals() dalla classe Object otterranno il comportamento equals() di default.

Se a e b sono di un tipo primitivo come, per esempio, int, boolean, etc., allora per il metodo assertEquals(a,b) viene effettuato quanto segue:
a e b sono convertite nei loro oggetti equivalenti (Integer, Boolean etc.,) e viene valutato su di essi a.equals( b ).

Assert di floating point

Quando si confrontano i tipi floating point (double o float), c’è un ulteriore parametro obbligatorio, delta.

L’assert valuta Math.abs( expected – actual )← delta per evitare errori di round-off nei casi di confronti tra tipi floating point.

Esempio:

assertEquals( aDouble, anotherDouble, 0.0001 )

I parametri dei metodi di assert

In ogni metodo di assert con due parametri, il primo parametro rappresenta il valore atteso, mentre il secondo parametro è il valore reale.

Qualsiasi metodo di assert può avere un ulteriore parametro di tipo String come primo parametro. Il valore di tale parametro verrà incluso nel messaggio che comparirà se l’assert fallisce.

Esempio:

fail( message )
assertEquals( message, expected, actual)

Exception testing

Aggiungere un parametro all’annotation @Test, indicando che ci si aspetta una particolare classe di eccezione diurante questo test.

@Test(expected=ExceptedTypeOfException.class)
public void testException()
{

exceptionCausingMethod();

}

Se non viene generata nessuna eccezione, o occorre una eccezione inaspettata, il test fallisce.
Cioè, arrivando alla fine del metodo senza nessuna eccezione causerà il fallimento del test case.

Testare il contenuto di un messaggio di eccezione, o limitare lo scope dell’eccezione utilizzandol’approccio mostrato in figura.

 

Catturare l’eccezione e utilizzo di fail() se non viene generata.

Catturare l'eccezione e utilizzo di fail() se non viene generata.


Test suite

Una classe test può invocare altre classi test creando test suites.

Una qualsiasi classe test può contenere un metodo statico nominato suite() all’interno del quale è possibile eseguire una qualsiasi collezione di test.

public static Test suite()

 

SetUp e TearDown

JUnit fornisce due metodi che possono essere sovrascritti al fine di poter costruire l’ambiente di testing (ex. inizializzare variabili, effettuare connessioni al database ecc) prima di eseguire tutti i test o pulire (ex. chiudere la connessione al database) dopo che tutti i test sono stati eseguiti:

 

protected void setUp()

protected void tearDown()

Il metodo setUp verrà chiamato prima che tutti i metodi testxxx siano eseguiti e viene utilizzato ad esempio per inizializzare variabili o stabilire connessioni al database.

Il metodo tearDown verrà chiamato dopo che tutti i metodi testxxx sono stati eseguiti ed esempio per liberare memoria o chiudere connessioni al database.

Esempio.

Esempio.

JUnit Design.

JUnit Design.


JUnit Framework (v3)

Tutto quanto visto finora vale per JUnit versione 4.
Per la versione 3.x, abbiamo delle differenze.
Non possiamo utilizzare direttamente i metodi assert nel nostro codice del progetto, ma c’è bisogno di un progetto di test, che si basa su Junit.

Importiamo le necessarie classi JUnit.

Ogni classe che effettua un test eredita da TestCase. La classe base TestCase contiene la maggior parte delle funzionalità necessarie per poter effettuare Unit testing, compresi i metodi assert.

Abbiamo bisogno di un costruttore al quale passiamo una stringa, ed effettuiamo una chiamata al costruttore super.

La classe contiene metodi nominati con test… Tutti i metodi che hanno nomi che iniziano con test… saranno automaticamente eseguiti da Junit 3.x, tutti quelli annotati con @test saranno eseguiti da Junit 4.

 

Progetto di test.

Progetto di test.


Best Practices

TestCases (classi Java)

  • Di solito esegue il test di una singola classe Java.
  • Inizia o finisce con il nome Test.

Tests (metodi Java) - test di un singolo metodo

  1. testXxx test del metodo xxx.
  2. Test multipli del metodo xxx hanno come nome testXxxYyy, dove Yyy descrive una particolare condizione, ad esempio, testEndsWithEmpty.

Etichettare gli assert.

Eseguire un test separato in ogni metodo testXxxx.

Eseguire i test utilizzando tools: IDE, Ant, Maven…

I test devono essere indipendenti.

Package uguali, directory differenti.

Package uguali, directory differenti

Package uguali, directory differenti.

Package uguali, directory differenti.


  • Contenuti protetti da Creative Commons
  • Feed RSS
  • Condividi su FriendFeed
  • Condividi su Facebook
  • Segnala su Twitter
  • Condividi su LinkedIn
Progetto "Campus Virtuale" dell'Università degli Studi di Napoli Federico II, realizzato con il cofinanziamento dell'Unione europea. Asse V - Società dell'informazione - Obiettivo Operativo 5.1 e-Government ed e-Inclusion