Formation Java / Spring Boot pour développeur.euse.s COBOL
À l’issue de cette quatorzième journée, vous serez capable de :
Aujourd’hui, on démystifie totalement les tests. L’objectif est de sécuriser le code métier.
Code pour Maven pour remplacer JaCoCo par Emma :
Ne pas tout copier.
Dans properties :
<properties> <!-- Version d'Emma --> <emma.version>2.1.5320</emma.version> </properties>
Dans dependencies :
<dependency> <groupId>emma</groupId> <artifactId>emma</artifactId> <version>${emma.version}</version> <scope>test</scope> </dependency>
Dans build puis plugins :
<!-- Plugin Emma pour la couverture de code --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>emma-maven-plugin</artifactId> <version>1.0-alpha-3</version> <configuration> <sourcePath>${project.build.sourceDirectory}</sourcePath> </configuration> <executions> <execution> <phase>process-test-classes</phase> <goals> <goal>instrument</goal> </goals> </execution> </executions> </plugin> <!-- Plugin Surefire pour exécuter les tests --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.2.2</version> <configuration> <argLine>-Demma.coverage.out.file=${project.build.directory}/coverage.em -Demma.coverage.out.merge=true </argLine> </configuration> </plugin>
Ensuite pour générer le rapport de couverture :
mvn emma:instrument # puis mvn test # puis le rapport mvn emma:report
Le rapport sera généré dans le dossier target/site/emma.
target/site/emma
Problèmes :
On écrit des programmes qui testent d’autres programmes.
Ces tests :
Les tests ne remplacent pas le métier, ils le protègent.
Un test unitaire :
On peut traduire une unité par :
un test avec une vraie base de données
Tester n’est pas optionnel.
JUnit est :
Nous ne détaillons pas Maven aujourd’hui, mais sachez que JUnit est ajouté comme dépendance de test.
JUnit est ajouté comme dépendance de test
@Test void nom_du_test() { // préparation // action // vérification }
Cette structure est fondamentale et se répête pour tous les types de tests.
AAA = Arrange – Act – Assert (Préparation - Action - Vérification)
@Test void debit_refuse_si_solde_insuffisant() { // premier A : Arrange Compte compte = new CompteEpargne("FR001", new BigDecimal("100")); // les autres A : Act et Assert assertThrows( IllegalStateException.class, () -> compte.debiter(new BigDecimal("200")) ); }
public void crediter(BigDecimal montant) { if (montant.compareTo(BigDecimal.ZERO) <= 0) { throw new IllegalArgumentException("Montant invalide"); } solde = solde.add(montant); }
On utilise des annotations @Test.
@Test
@Test void credit_accepte_montant_positif() { Compte compte = new CompteEpargne("FR001", new BigDecimal("100")); compte.crediter(new BigDecimal("50")); assertEquals(new BigDecimal("150"), compte.getSolde()); }
try { compte.debiter(...); fail(); } catch (...) { }
@Test void debit_interdit_si_solde_insuffisant() { Compte compte = new CompteEpargne("FR001", new BigDecimal("100")); assertThrows(IllegalStateException.class, () -> compte.debiter(new BigDecimal("200")) ); }
public class CompteService { private CompteDAO dao; public CompteService(CompteDAO dao) { this.dao = dao; } public void debiterCompte(String numero, BigDecimal montant) { Compte compte = dao.trouverParNumero(numero); compte.debiter(montant); dao.sauvegarder(compte); } }
Mockito permet de :
Un mock n’exécute pas de vraie logique et retourne ce qu’on lui indique.
CompteDAO dao = mock(CompteDAO.class); // on utilise notre interface DAO
when(dao.trouverParNumero("FR001")).thenReturn(new CompteEpargne("FR001", new BigDecimal("500")));
ça veut dire, Quand on appelle cette méthode, retourne tel résultat
Quand on appelle cette méthode, retourne tel résultat
@Test void debit_compte_appelle_sauvegarde() { CompteDAO dao = mock(CompteDAO.class); Compte compte = new CompteEpargne("FR001", new BigDecimal("500")); when(dao.trouverParNumero("FR001")).thenReturn(compte); CompteService service = new CompteService(dao); service.debiterCompte("FR001", new BigDecimal("100")); assertEquals(new BigDecimal("400"), compte.getSolde()); verify(dao).sauvegarder(compte); }
Ici, on teste :
verify(dao).sauvegarder(compte);
@ParameterizedTest @ValueSource(strings = {"-10", "0"}) void credit_refuse_montant_invalide(String montant) { Compte compte = new CompteEpargne("FR001", new BigDecimal("100")); assertThrows( IllegalArgumentException.class, () -> compte.crediter(new BigDecimal(montant)) ); }
Très utile pour tester des règles métier.
Compte
Vous pouvez aussi prendre des classes dans les différents projets déjà réalisés lors des séances précédentes.
Consignes (si c’est une classe Compte) :
CompteService
Consignes :
À ce stade :
Le projet est maintenant prêt pour Spring Boot.
Vous savez maintenant :
On marquera une nouvelle étape :
Grâce aux tests, Spring Boot ne sera pas une boîte noire.
Lien vers les la page des exercices sur les tests et TDD