Pubblicato il 16/03/2024 da alnao nella categoria Java & Spring Boot

In informatica, nello sviluppo software, il test-driven development, spesso abbreviato con la sigla TDD, è un modello di sviluppo del software che prevede che la stesura dei test automatici avvenga prima di quella del software e che lo sviluppo del software applicativo sia orientato esclusivamente all’obiettivo di passare i test automatici precedentemente predisposti. Il TDD prevede la ripetizione di un breve ciclo di sviluppo in tre fasi: nella prima fase viene scritto un test automatico, nella seconda fase viene sviluppata la quantità minima di codice necessaria per passare il test e nella terza fase viene eseguito un refactoring del codice per rispettare i livelli di qualità e leggibilità richiesti (fonte wiki).

Questo articolo non vuole essere una spiegazione accademica di cosa sono i TDD e come dovrebbero essere implementati, ma vuole essere solo una descrizione di una modalità di sviluppo con esempi pratici funzionanti di una delle tecniche più usate per realizzare TDD efficaci con il linguaggio Java. I test di questo tipo di TDD sono tutti atomici e devono verificare una sola operazione del requisito e, all’interno di ogni test, devono essere sviluppati tre passi:

  • Arrange: la definizione di tutti i dati di partenza
  • Act: la chiamata alla operazione da testare (di solito è una sola operazione ma può essere anche multipla)
  • Assert: la validazione del risultato confrontando la risposta del passo 2 con quanto definito nel punto 1

Nei progetti Java creati con Maven, in fase di creazione del progetto è presente la configurazione dei test con una classe vuota, lanciando il comando

$ mvn test

il sistema maven eseguirà tutte le classi di test presenti nel progetto, se il comando viene lanciato da riga di comando il risultato dei test viene espost con i messaggi:

[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, 
Time elapsed: 0.033 s - in it.alnao.examples.AppTest

oppure un messaggio di errore in caso di test non passato con il messaggio

[INFO] Running it.alnao.examples.AppTest
[ERROR] Tests run: 4, Failures: 1, Errors: 0, Skipped: 1, Time elapsed: 0.031 s <<< FAILURE! - in it.alnao.examples.AppTest
[ERROR] dividiPerZero Time elapsed: 0.01 s <<< FAILURE!
org.opentest4j.AssertionFailedError: Expected java.lang.ArithmeticException to be thrown, but nothing was thrown.
at it.alnao.examples.AppTest.dividiPerZero(AppTest.java:40)

Mentre nei programmi IDE (come Visual studio Code e/o Eclipse) sono presenti diversi tool grafici e viste che permettono di eseguire i test e vedere il risultato in maniera grafica:

E’ possibile anche generare un report di maven e si usa il comando

$ mvn site

però è necessario censitre nel pom.xml una sezione dedicata di reporting:

<reporting>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-report-plugin</artifactId>
    </plugin>
  </plugins>
</reporting>

La libreria comunemente più usata è quella JUnit alla versione 5 ma la maggior parte dei progetti creati con maven tramite archetipi è configurata con la 4 o con la 3, è consigliato sempre aggiornare il file di configurazione pom.xml di tutti i nuovi progetti impostando l’ultima versione di JUnit, gli esempi fanno riferimento alla versione 5 che ha modificato i package e le classi usate per eseguire il passo assert nelle test unit perché i metodi sono racchiusi nel pacakge:

import org.junit.jupiter.api.Assertions;

e in aggiunta bisogna ricordarsi che è necessario usare la versione 11 di Java.


Un esempio pratico: l’idea è di realizzare un “servizio” che effettui la divisione di due numeri, usando la metodologia TDD dobbiamo definire per prima i test che verificheranno il buon codice, per esempio possiamo partire dall’idea di verificare che il numero 5 diviso per il numero 2 dia come risultato due e mezzo e bisogna ricordare non è possibile eseguire divisioni se il divisore è zero, prevediamo un test specifico che verifichi la esecuzione di una ArithmeticException nel caso di divisore zero. Il codice di esempio di questi semplici test diventa:

@Test
@DisplayName("Dividi 42")
public void dividi42() throws Exception{
  //1) Arrange: la definizione di tutti i dati di partenza 
  Double dividendo=new Double(42.0);
  Double divisore=new Double(16.0);
  Double resultAtteso=new Double(2.625);
  //2) Act: la chiamata alla operazione da testare (di solito è una sola operazione ma può essere anche multipla)
  Double result=App.dividi(dividendo, divisore);
  //3) Assert: la validazione del risultato confrontando la risposta del passo 2 con quanto definito nel punto 1 
  Assertions.assertEquals(resultAtteso,result);
}
@Test
@DisplayName("Dividi per zero")
public void dividiPerZero(){
  //1) Arrange: la definizione di tutti i dati di partenza 
  final Double dividendo=new Double(5.0);
  final Double divisore=new Double(0);
  //3) Assert: la validazione del risultato confrontando la risposta del passo 2 con quanto definito nel punto 1 
  Assertions.assertThrows(ArithmeticException.class,
    ()->{ //2) Act: la chiamata alla operazione da testare (di solito è una sola operazione ma può essere anche multipla)
      App.dividi(dividendo, divisore);
    }
  );
}
@Test
public void shouldAnswerWithTrue() {
  Assertions.assertTrue( true );
}
@Test
@Disabled("Disabled test example")
  void disabledTest() {
  fail();
}

In questo semplice esempio sono stati usati i metodi:

  • assertEquals per confrontare il ritorno del metodo con il valore atteso
  • assertThrows per verificare che venga effettivamente lanciata l’exception corretta nel caso di divisore pari a zero
  • assertTrue per una verifica statica, può essere usato per per far fallire automaticamente un test in un ramo di codice che non deve essere eseguito con il parametro false.

Inoltre negli esempi si possono notare tutte diverse le annotazioni messe a disposizione dalla libreria, l’elenco completo di tutti i metodi disponibili e le varie annotazioni si può trovare alla documentazione ufficiale. L’esempio completo e funzionante di questo semplice metodo può essere trovato al solito repository:

https://github.com/alnao/JavaExamples/tree/master/GenericJava/14testJUnit5

ma è possibile consultare anche un repository ricco di esempi:

https://github.com/eugenp/tutorials/blob/master/testing-modules/junit-5-basics/pom.xml
MENU