Aller au contenu

Semaine 3 : JOUR 4 – TESTS UNITAIRES EN PROFONDEUR (JUnit 5 & Mockito)

Formation Java / Spring Boot pour développeur.euse.s COBOL


Objectifs pédagogiques

À 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.


Liens vers les différents cours sur les Tests

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.

contexte (COBOL / Tests Java)

En COBOL, comment testez-vous ?


En Java

On écrit des programmes qui testent d’autres programmes.

Ces tests :

Les tests ne remplacent pas le métier, ils le protègent.


1) Qu’est-ce qu’un test unitaire ?

Définition claire

Un test unitaire :

On peut traduire une unité par :


2) Un test unitaire n’est PAS


3) Pourquoi tester ?

Raisons techniques

Raisons métier

Tester n’est pas optionnel.


4) JUnit 5 – Présentation progressive

JUnit est :


Dépendance

Nous ne détaillons pas Maven aujourd’hui, mais sachez que JUnit est ajouté comme dépendance de test.


5) Structure d’un test JUnit 5

@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.


6) La règle des 3A (AAA)

AAA = Arrange – Act – Assert (Préparation - Action - Vérification)

Exemple

@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"))
    );
}

7) Tester une règle métier simple

Code métier à tester

public void crediter(BigDecimal montant) {
    if (montant.compareTo(BigDecimal.ZERO) <= 0) {
        throw new IllegalArgumentException("Montant invalide");
    }
    solde = solde.add(montant);
}

Test correspondant

On utilise des annotations @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());
}

8) Tester une exception

Mauvaise pratique (à éviter)

try {
    compte.debiter(...);
    fail();
} catch (...) { }

Bonne pratique (JUnit 5)

@Test
void debit_interdit_si_solde_insuffisant() {
    Compte compte = new CompteEpargne("FR001", new BigDecimal("100"));

    assertThrows(IllegalStateException.class, () -> compte.debiter(new BigDecimal("200"))
    );
}

9) Tester un service métier

Service à tester

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);
    }
}

10) Introduction à Mockito (sans peur)

Qu’est-ce que Mockito ?

Mockito permet de :

Un mock n’exécute pas de vraie logique et retourne ce qu’on lui indique.


11) Créer un mock avec Mockito

CompteDAO dao = mock(CompteDAO.class); // on utilise notre interface DAO

12) Définir le comportement d’un mock

when(dao.trouverParNumero("FR001")).thenReturn(new CompteEpargne("FR001", new BigDecimal("500")));

ça veut dire, Quand on appelle cette méthode, retourne tel résultat


13) Tester le service avec un mock

@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 :


14) Vérifier les interactions

verify(dao).sauvegarder(compte);

15) Tests paramétrés

@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.


Travaux pratiques

TP 1 – Tests de la classe 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) :


TP 2 – Tests du service CompteService ou autre Service

Consignes :


TP 3 – Lecture de tests existants

Consignes :


Section spéciale – Erreurs fréquentes

  1. Tester trop large
  2. Tester plusieurs règles dans un seul test
  3. Tester l’implémentation au lieu du comportement
  4. Ne pas nommer les tests clairement
  5. Oublier les cas d’erreur
  6. Utiliser Mockito partout
  7. Ne pas vérifier les interactions
  8. Avoir peur des mocks
  9. Confondre test unitaire et test d’intégration
  10. Dire “ça marche sans tests”

Avancement du projet

À ce stade :

Le projet est maintenant prêt pour Spring Boot.


Synthèse de la journée

Vous savez maintenant :


Préparation pour la semaine suivante

On marquera une nouvelle étape :

Grâce aux tests, Spring Boot ne sera pas une boîte noire.

Travaux pratiques sur les tests et TDD

Lien vers les la page des exercices sur les tests et TDD