Contactez-nous

Ecrire des cas de test (assertions)

Apprenez à rédiger des cas de test efficaces en Python. Utilisez les assertions (assertEqual, assertTrue, assertRaises, etc.) pour vérifier le comportement de votre code. Découvrez les bonnes pratiques pour des tests clairs, concis et pertinents.

Structure d'une méthode de test : Arrange, Act, Assert (AAA)

Une méthode de test (dans une classe de test héritant de `unittest.TestCase`, ou une fonction de test avec d'autres frameworks comme `pytest`) vérifie généralement un *cas d'utilisation* spécifique d'une unité de code (fonction, méthode, classe).

Une bonne pratique pour structurer une méthode de test est de suivre le modèle AAA :

  • Arrange (Préparer) : Mettre en place les conditions initiales du test. Cela peut inclure la création d'objets, l'initialisation de variables, la configuration de données de test, etc.
  • Act (Agir) : Exécuter le code que vous voulez tester (appeler la fonction ou la méthode à tester, avec les arguments appropriés).
  • Assert (Vérifier) : Vérifier que le résultat de l'action est conforme à ce qui est attendu. Cela se fait en utilisant des *assertions* (par exemple, `self.assertEqual()`, `self.assertTrue()`, etc. dans `unittest`).

Exemple :

import unittest

def additionner(a, b):
    return a + b

class TestAddition(unittest.TestCase):
    def test_addition(self):
        # Arrange (Préparer)
        a = 2
        b = 3

        # Act (Agir)
        resultat = additionner(a, b)

        # Assert (Vérifier)
        self.assertEqual(resultat, 5)

Cette structure en trois étapes rend les tests plus clairs, plus faciles à comprendre et à maintenir.

Choisir les bonnes assertions : assertEqual, assertTrue, assertRaises, etc.

Le module `unittest` (et d'autres frameworks de test comme `pytest`) fournit de nombreuses méthodes d'assertion pour vérifier différents types de conditions.

Il est important de choisir l'assertion la plus appropriée pour chaque situation, afin de rendre le test clair et expressif.

Voici quelques exemples d'assertions courantes (voir la section précédente pour une liste plus complète) :

  • `self.assertEqual(a, b)` : Vérifie que `a` est égal à `b`. Utilisez cette assertion pour comparer des nombres, des chaînes de caractères, des listes, des tuples, des dictionnaires, etc.
  • `self.assertTrue(x)` : Vérifie que `x` est vrai (booléen). Utilisez cette assertion pour vérifier des conditions booléennes.
  • `self.assertFalse(x)` : Vérifie que `x` est faux.
  • `self.assertIsNone(x)` : Vérifie que `x` est `None`.
  • `self.assertIn(a, b)` : Vérifie que `a` est présent dans `b` (où `b` est une séquence, comme une liste, un tuple, ou une chaîne).
  • `self.assertIsInstance(a, b)` : Vérifie que `a` est une instance de la classe `b` (ou d'une sous-classe de `b`).
  • `self.assertRaises(exception, callable, *args, **kwargs)` : Vérifie que l'appel de `callable(*args, **kwargs)` lève l'exception spécifiée.

Exemple (utilisation de différentes assertions) :

import unittest

class TestExemple(unittest.TestCase):
    def test_liste(self):
        ma_liste = [1, 2, 3]
        self.assertEqual(len(ma_liste), 3)  # Vérifie la longueur
        self.assertIn(2, ma_liste)         # Vérifie la présence d'un élément
        self.assertTrue(ma_liste[0] == 1)   # Vérifie une condition booléenne

    def test_division(self):
        with self.assertRaises(ZeroDivisionError):  # Vérifie qu'une exception est levée
            10 / 0

Choisissez l'assertion qui correspond le mieux à ce que vous voulez vérifier. Cela rendra vos tests plus clairs et plus faciles à comprendre.

Bonnes pratiques pour rédiger des cas de test

Voici quelques bonnes pratiques pour rédiger des cas de test efficaces :

  • Testez un seul comportement par méthode de test : Chaque méthode de test doit se concentrer sur un seul aspect du comportement de l'unité de code testée. Cela rend les tests plus faciles à comprendre et à déboguer.
  • Utilisez des noms de méthodes de test descriptifs : Le nom de la méthode de test doit indiquer clairement ce qui est testé. Par exemple, `test_addition_nombres_positifs`, `test_division_par_zero`, etc.
  • Couvrez tous les cas d'utilisation importants : Testez les cas normaux, les cas limites, et les cas d'erreur.
  • Utilisez des données de test variées : Testez avec différentes valeurs d'entrée pour vous assurer que votre code fonctionne correctement dans toutes les situations.
  • Rendez vos tests indépendants : Un test ne doit pas dépendre du résultat d'un autre test. Chaque test doit être autonome et pouvoir être exécuté dans n'importe quel ordre.
  • Gardez vos tests courts et simples : Les tests doivent être faciles à lire et à comprendre. Evitez les tests trop longs ou trop complexes.
  • Utilisez des commentaires si nécessaire : Si un test est particulièrement complexe, ajoutez des commentaires pour expliquer ce qu'il fait.
  • N'hésitez pas à refactoriser vos tests : Comme le code de production, le code de test doit être maintenu et refactorisé si nécessaire.
  • Vérifiez les cas de non-régression : Quand vous corrigez un bug, ajoutez un test pour vous assurer qu'il ne réapparaîtra pas.
  • Utilisez un framework de test : `unittest` est un bon point de départ, mais d'autres frameworks comme `pytest` peuvent rendre l'écriture et l'exécution de tests plus faciles.

Exemple : tester une fonction de validation d'e-mail

Voici un exemple plus complet de cas de test pour une fonction qui valide une adresse e-mail (en utilisant une expression régulière simplifiée) :

import re
import unittest

def valider_email(email):
    """Valide une adresse e-mail (format simplifié)."""
    motif = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
    return bool(re.match(motif, email))


class TestValidationEmail(unittest.TestCase):

    def test_email_valide(self):
        self.assertTrue(valider_email("test@example.com"))
        self.assertTrue(valider_email("utilisateur.nom+alias@domaine.co.uk"))

    def test_email_sans_arobase(self):
        self.assertFalse(valider_email("testexample.com"))

    def test_email_sans_domaine(self):
        self.assertFalse(valider_email("test@"))

    def test_email_avec_caracteres_invalides(self):
        self.assertFalse(valider_email("test@!#$%^.com"))

    def test_email_vide(self):
      self.assertFalse(valider_email(""))


if __name__ == '__main__':
    unittest.main()

Dans cet exemple :

  • La fonction `valider_email` vérifie si une chaîne de caractères correspond à un format d'e-mail simplifié.
  • La classe `TestValidationEmail` contient plusieurs méthodes de test, chacune vérifiant un cas d'utilisation différent (e-mail valide, e-mail sans `@`, e-mail sans domaine, e-mail avec des caractères invalides, chaîne vide).
  • Les assertions `assertTrue` et `assertFalse` sont utilisées pour vérifier que la fonction `valider_email` retourne le résultat attendu.

Cet exemple illustre comment écrire des tests unitaires pour différents cas d'utilisation, en utilisant des assertions appropriées.