
Le module unittest
Découvrez le module 'unittest' de la bibliothèque standard Python, un framework complet pour écrire et exécuter des tests unitaires. Créez des classes de test, utilisez des assertions, et organisez vos tests.
unittest : un framework de test inspiré de JUnit
Le module `unittest` (parfois appelé "PyUnit") est un framework de test unitaire inclus dans la bibliothèque standard Python. Il est inspiré de JUnit (un framework de test pour Java).
`unittest` fournit des outils pour :
- Ecrire des tests unitaires de manière structurée (en utilisant des classes et des méthodes).
- Définir des assertions pour vérifier les résultats attendus.
- Regrouper les tests en suites de tests.
- Exécuter les tests et obtenir des rapports détaillés.
- Mettre en place un environnement de test (setup) et le nettoyer après les tests (teardown).
`unittest` est un framework puissant et flexible, mais il peut sembler un peu verbeux comparé à d'autres frameworks de test plus modernes (comme `pytest`). Cependant, il reste un outil important à connaître pour tout développeur Python.
Structure d'un test avec unittest : TestCase et méthodes de test
Pour écrire des tests unitaires avec `unittest`, vous devez :
- Créer une classe qui hérite de `unittest.TestCase`.
- Définir des méthodes de test à l'intérieur de cette classe. Le nom de chaque méthode de test doit commencer par `test`.
- Utiliser les méthodes d'assertion fournies par `unittest.TestCase` pour vérifier les résultats attendus.
Exemple :
import unittest
# Fonction à tester
def additionner(a, b):
return a + b
# Classe de test
class TestAddition(unittest.TestCase):
def test_addition_nombres_positifs(self):
resultat = additionner(2, 3)
self.assertEqual(resultat, 5) # Assertion
def test_addition_avec_zero(self):
resultat = additionner(5, 0)
self.assertEqual(resultat, 5)
# Exécution des tests (si le fichier est exécuté directement)
if __name__ == '__main__':
unittest.main()Dans cet exemple:
- `TestAddition` est une classe de test qui hérite de `unittest.TestCase`.
- `test_addition_nombres_positifs` et `test_addition_avec_zero` sont des méthodes de test. Leur nom commence par `test`.
- `self.assertEqual(resultat, 5)` est une assertion. Elle vérifie que `resultat` est égal à 5. Si ce n'est pas le cas, le test échoue.
Méthodes d'assertion : vérifier les résultats
`unittest.TestCase` fournit de nombreuses méthodes d'assertion pour vérifier différentes conditions. Voici quelques-unes des méthodes les plus courantes :
- `assertEqual(a, b)` : Vérifie que `a == b`.
- `assertNotEqual(a, b)` : Vérifie que `a != b`.
- `assertTrue(x)` : Vérifie que `x` est vrai (booléen).
- `assertFalse(x)` : Vérifie que `x` est faux.
- `assertIs(a, b)` : Vérifie que `a is b` (même objet).
- `assertIsNot(a, b)` : Vérifie que `a is not b`.
- `assertIsNone(x)` : Vérifie que `x` est `None`.
- `assertIsNotNone(x)` : Vérifie que `x` n'est pas `None`.
- `assertIn(a, b)` : Vérifie que `a` est dans `b` (`a in b`).
- `assertNotIn(a, b)` : Vérifie que `a` n'est pas dans `b`.
- `assertIsInstance(a, b)` : Vérifie que `a` est une instance de `b` (ou d'une sous-classe de `b`).
- `assertNotIsInstance(a, b)` : Vérifie que `a` n'est pas une instance de `b`.
- `assertRaises(exception, callable, *args, **kwds)` : Vérifie que l'appelable `callable` lève l'exception `exception` lorsqu'il est appelé avec les arguments donnés.
Il existe de nombreuses autres méthodes d'assertion (voir la documentation de `unittest` pour la liste complète).
Si une assertion échoue, la méthode de test est interrompue et le test est considéré comme ayant échoué.
Exemple :
import unittest
class TestChaine(unittest.TestCase):
def test_longueur(self):
chaine = "Bonjour"
self.assertEqual(len(chaine), 7)
def test_vide(self):
chaine = ''
self.assertTrue(len(chaine) == 0)
def test_dans(self):
chaine = 'abcde'
self.assertIn('bc', chaine)
def test_exception(self):
with self.assertRaises(TypeError):
resultat = len(5) # len() ne peut pas être appelé sur un entier
Exécuter les tests : unittest.main()
Pour exécuter les tests, vous pouvez utiliser la fonction `unittest.main()`.
Placez le code suivant à la fin de votre fichier de test :
if __name__ == '__main__':
unittest.main()Lorsque vous exécutez le fichier Python (par exemple, `python mon_fichier_de_test.py`), `unittest.main()` :
- Découvre automatiquement les classes de test (les classes qui héritent de `unittest.TestCase`) et les méthodes de test (les méthodes dont le nom commence par `test`) dans le fichier.
- Exécute les tests.
- Affiche un rapport des résultats (quels tests ont réussi, quels tests ont échoué, et pourquoi).
`unittest.main()` accepte plusieurs arguments pour contrôler l'exécution des tests (voir la documentation).
Vous pouvez également exécuter les tests depuis la ligne de commande, en utilisant le module `unittest` comme un outil :
python -m unittest mon_fichier_de_test.pyPour découvrir automatiquement tous les tests dans un répertoire et ses sous-répertoires, vous pouvez utiliser :
python -m unittest discoversetUp et tearDown : initialisation et nettoyage
Si vous avez du code d'initialisation (setup) à exécuter *avant* chaque test (par exemple, créer des objets, ouvrir des fichiers, etc.), et/ou du code de nettoyage (teardown) à exécuter *après* chaque test (par exemple, supprimer des objets, fermer des fichiers, etc.), vous pouvez utiliser les méthodes spéciales `setUp` et `tearDown`.
- `setUp(self)` : Cette méthode est appelée *avant* l'exécution de chaque méthode de test.
- `tearDown(self)` : Cette méthode est appelée *après* l'exécution de chaque méthode de test, *même si le test a échoué*.
Exemple :
import unittest
class TestExemple(unittest.TestCase):
def setUp(self):
# Code d'initialisation (exécuté avant chaque test)
print("Mise en place de l'environnement de test...")
self.liste = [1, 2, 3]
def tearDown(self):
# Code de nettoyage (exécuté après chaque test)
print("Nettoyage de l'environnement de test...")
del self.liste
def test_premier_element(self):
self.assertEqual(self.liste[0], 1)
def test_longueur(self):
self.assertEqual(len(self.liste), 3)Dans cet exemple, `setUp` est appelé avant chaque méthode de test (et crée une liste), et `tearDown` est appelé après chaque méthode de test (et supprime la liste).
Il existe aussi les méthodes de classe : `setUpClass` et `tearDownClass` qui sont appelées respectivement avant et après l'exécution des tests de la classe.
L'utilisation de `setUp` et `tearDown` permet de s'assurer que chaque test s'exécute dans un environnement propre et isolé, et que les ressources sont correctement libérées.
Suites de tests
Il est possible d'organiser les tests en suite de tests avec la classe `TestSuite`. On peut ainsi organiser les tests hiérarchiquement, ou décider de n'en lancer qu'une partie.
import unittest
#On reprend les tests de l'exemple précédent
class TestAddition(unittest.TestCase):
...
class TestChaine(unittest.TestCase):
...
#On crée une suite
suite = unittest.TestSuite()
#On ajoute des tests spécifiques à la suite
suite.addTest(TestAddition('test_addition_nombres_positifs'))
#On peut aussi ajouter tous les tests d'une classe
suite.addTest(unittest.makeSuite(TestChaine))
#On lance la suite
runner = unittest.TextTestRunner()
runner.run(suite)