Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 36

Assertions JUnit : valider le comportement du code

Recommended resources

Bonjour ! Dans la leçon précédente, nous avons écrit notre première classe de test et utilisé l'assertion assertEquals pour valider un résultat. Vous avez ainsi appris le principe fondamental de la vérification automatisée : comparer un résultat attendu à un résultat réel.

Aujourd'hui, nous allons considérablement enrichir votre boîte à outils de testeur. L'objectif est de maîtriser les assertions les plus courantes de JUnit, telles que assertTrue, assertFalse et assertNotNull. En choisissant l'assertion la plus appropriée pour chaque situation, vous rendrez vos tests non seulement plus puissants, mais aussi beaucoup plus clairs et expressifs.

1. La palette d'assertions de JUnit

assertEquals est parfait pour vérifier une égalité, mais que faire si vous voulez vérifier qu'une valeur est vraie (true), qu'un objet n'est pas nul, ou que deux tableaux contiennent les mêmes éléments ? JUnit fournit une famille complète de méthodes d'assertion pour couvrir tous ces cas de figure.

Toutes ces méthodes sont des méthodes statiques de la classe org.junit.jupiter.api.Assertions. C'est pourquoi nous les importons statiquement pour un code plus concis :
import static org.junit.jupiter.api.Assertions.*;

La vidéo suivante présente un excellent panorama des assertions les plus utiles.

Tutoriel Java - tests unitaires

Cette vidéo de la chaîne EvoluNoob, que nous avons déjà vue, détaille plusieurs méthodes d'assertion essentielles.

Regardez attentivement le segment sur les méthodes d'assertion. L'auteur y présente : assertEquals (que vous connaissez déjà) assertTrue et assertFalse pour les conditions booléennes assertSame et assertNotSame pour vérifier l'identité des objets assertNull et assertNotNull pour les vérifications de nullité Notez la structure commune : on teste une condition et on peut fournir un message qui s'affichera en cas d'échec.

Pour résumer et organiser ces informations, voici un tableau des assertions les plus courantes :

Catégorie Méthode Description
Égalité assertEquals(expected, actual) Vérifie que deux valeurs sont égales (utilise la méthode .equals() pour les objets).
assertNotEquals(unexpected, actual) Vérifie que deux valeurs ne sont pas égales.
Booléen assertTrue(condition) Vérifie que la condition est true.
assertFalse(condition) Vérifie que la condition est false.
Nullité assertNull(object) Vérifie que l'objet est null.
assertNotNull(object) Vérifie que l'objet n'est pas null.
Identité assertSame(expected, actual) Vérifie que deux variables référencent exactement le même objet en mémoire (==).
assertNotSame(unexpected, actual) Vérifie que deux variables référencent des objets différents.
Tableaux assertArrayEquals(expected, actual) Vérifie que deux tableaux sont égaux (même longueur et mêmes éléments dans le même ordre).

2. Focus sur les assertions clés

Approfondissons quelques-unes de ces assertions avec des exemples concrets. Pour cela, imaginons que nous testions un service de gestion d'utilisateurs.

// Classe que nous allons tester
public class User {
    private final String username;
    private final boolean isAdmin;

    public User(String username, boolean isAdmin) {
        this.username = username;
        this.isAdmin = isAdmin;
    }

    public String getUsername() { return username; }
    public boolean isAdmin() { return isAdmin; }

    // NOTE: equals() et hashCode() sont nécessaires pour que assertEquals fonctionne correctement sur les objets.
    // Votre IDE peut les générer pour vous.
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return isAdmin == user.isAdmin && java.util.Objects.equals(username, user.username);
    }
}

// Service à tester
public class UserService {
    public User findByUsername(String username) {
        if ("admin".equals(username)) {
            return new User("admin", true);
        }
        return null; // Retourne null si l'utilisateur n'est pas trouvé
    }
}

Vérifications de nullité : assertNull et assertNotNull

Ces assertions sont fondamentales pour vérifier le comportement de méthodes qui peuvent retourner null, comme notre findByUsername.

// Dans UserServiceTest.java
@Test
void testFindByUsername() {
    UserService userService = new UserService();

    // Cas 1: L'utilisateur existe
    User admin = userService.findByUsername("admin");
    assertNotNull(admin, "L'utilisateur 'admin' ne devrait pas être null.");

    // Cas 2: L'utilisateur n'existe pas
    User unknown = userService.findByUsername("unknown");
    assertNull(unknown, "Un utilisateur inconnu devrait être null.");
}

Vérifications booléennes : assertTrue et assertFalse

Idéal pour tester des méthodes qui retournent un boolean, comme la méthode isAdmin() de notre objet User.

@Test
void testUserPermissions() {
    User adminUser = new User("admin", true);
    User regularUser = new User("guest", false);

    assertTrue(adminUser.isAdmin(), "L'utilisateur admin devrait avoir les droits d'admin.");
    assertFalse(regularUser.isAdmin(), "L'utilisateur guest ne devrait pas avoir les droits d'admin.");
}
Cette image montre un exemple de code utilisant `assertTrue` et le résultat d'exécution positif dans l'IDE, confirmant que le test est passé avec succès.

Égalité de contenu vs. identité d'objet : assertEquals vs. assertSame

C'est une distinction cruciale en Java qui se reflète dans les tests.

  • assertEquals(obj1, obj2) vérifie si obj1.equals(obj2) est vrai. Les objets peuvent être différents en mémoire mais avoir le même "contenu".
  • assertSame(obj1, obj2) vérifie si obj1 == obj2 est vrai. Les deux variables doivent pointer vers la même instance en mémoire.
@Test
void testObjectEqualityAndIdentity() {
    User user1 = new User("alex", false);
    User user2 = new User("alex", false); // Même contenu, mais nouvelle instance
    User user3 = user1; // Même instance

    // Vérification d'égalité de contenu
    assertEquals(user1, user2, "user1 et user2 devraient être égaux en contenu.");

    // Vérification d'identité (référence mémoire)
    assertNotSame(user1, user2, "user1 et user2 ne devraient pas être la même instance.");
    assertSame(user1, user3, "user1 et user3 devraient être la même instance.");
}

3. Grouper les assertions avec assertAll

Par défaut, dès qu'une assertion échoue dans une méthode de test, l'exécution de cette méthode s'arrête. C'est souvent ce que l'on veut, mais parfois, il est utile de vérifier plusieurs propriétés d'un même objet et de voir tous les échecs en une seule fois. C'est le rôle de assertAll.

La vidéo suivante explique très bien ce concept.

Apprendre le Java #21 Les Tests Unitaire - JUnit5

Cette courte vidéo de Graven - Développement montre de manière très claire l'intérêt et l'utilisation de assertAll.

Regardez le passage sur assertAll. Observez comment il permet d'exécuter plusieurs assertions et de rapporter toutes les erreurs simultanément.

Voici comment nous pourrions l'utiliser pour tester notre objet User :

@Test
void testAdminUserProperties() {
    UserService userService = new UserService();
    User admin = userService.findByUsername("admin");

    assertNotNull(admin); // Vérifions d'abord que l'objet n'est pas null

    // Ensuite, vérifions toutes ses propriétés en même temps
    assertAll("Propriétés de l'utilisateur admin",
        () -> assertEquals("admin", admin.getUsername(), "Le nom d'utilisateur est incorrect."),
        () -> assertTrue(admin.isAdmin(), "L'utilisateur devrait être un admin.")
    );
}

L'avantage est que si le nom d'utilisateur ET le statut d'admin sont incorrects, JUnit vous signalera les deux erreurs, vous faisant gagner du temps de débogage.

Cette image illustre une suite de tests réussis dans un IDE. Vous pouvez y voir une liste de tests validant différentes assertions, ce qui vous donne un aperçu de ce que vous obtiendrez en exécutant vos propres tests.

4. Exercice pratique : Test d'un service de produits

Mettez en pratique ce que vous venez d'apprendre. Vous allez tester un service simple qui gère des produits.

Les classes à créer :

  1. Dans src/main/java/com/example/, créez la classe Product :

    public class Product {
        private final long id;
        private final String name;
        private double price;
        private boolean inStock;
    
        public Product(long id, String name, double price, boolean inStock) {
            this.id = id;
            this.name = name;
            this.price = price;
            this.inStock = inStock;
        }
    
        // Générez les getters pour tous les champs.
        // Générez aussi equals() et hashCode() pour que assertEquals fonctionne.
    }
    
  2. Dans le même package, créez la classe ProductService :

    import java.util.HashMap;
    import java.util.Map;
    
    public class ProductService {
        private final Map<Long, Product> products = new HashMap<>();
    
        public ProductService() {
            // Ajout de quelques produits pour les tests
            products.put(1L, new Product(1L, "Laptop Pro", 1200.00, true));
            products.put(2L, new Product(2L, "Souris Gamer", 75.50, false));
        }
    
        public Product findById(long id) {
            return products.get(id);
        }
    }
    

Votre mission :

Créez une classe de test ProductServiceTest dans src/test/java/com/example/ et écrivez les méthodes de test suivantes :

  1. testFindExistingProduct() :

    • Vérifiez qu'un produit existant (ex: ID 1) est bien retourné (assertNotNull).
    • Utilisez assertAll pour vérifier que le nom, le prix et le statut inStock du produit retourné sont corrects. Utilisez assertEquals et assertTrue.
  2. testFindUnavailableProduct() :

    • Vérifiez qu'un produit existant mais hors stock (ex: ID 2) est bien retourné (assertNotNull).
    • Vérifiez que son statut inStock est bien false en utilisant assertFalse.
  3. testFindNonExistingProduct() :

    • Vérifiez que la recherche d'un produit qui n'existe pas (ex: ID 99) retourne null en utilisant assertNull.

Prenez le temps de réaliser cet exercice. C'est en pratiquant que ces concepts s'ancrent durablement.


Conclusion

Vous disposez maintenant d'un solide arsenal d'assertions pour valider avec précision le comportement de votre code. Savoir choisir la bonne assertion (assertTrue pour un booléen, assertNull pour une absence d'objet, assertEquals pour une valeur) est une compétence clé qui rend vos tests plus robustes et plus faciles à comprendre pour vous et vos futurs collaborateurs.

Les points clés à retenir :

  • Utilisez assertNotNull et assertNull pour les cas où une méthode peut retourner (ou non) un objet.
  • Utilisez assertTrue et assertFalse pour valider des conditions ou des méthodes retournant un booléen.
  • Faites la distinction entre assertEquals (égalité de valeur) et assertSame (identité de référence).
  • Utilisez assertAll pour regrouper des vérifications sur un même objet et obtenir un rapport complet de toutes les erreurs.

Dans la prochaine leçon, nous aborderons le cycle de vie des tests. Vous apprendrez à utiliser les annotations @BeforeEach et @AfterEach pour initialiser et nettoyer l'état de vos tests, une technique essentielle pour écrire des tests propres et indépendants.