Contactez-nous

Tests unitaires et débogage

Découvrez les tests unitaires en Python avec les modules unittest et pytest. Apprenez à écrire des cas de test (assertions), à utiliser des mocks et des patchs (unittest.mock), et à exécuter les tests. Maîtrisez le débogage avec pdb et les outils de débog

Pourquoi écrire des tests unitaires ? Fiabilité, maintenabilité et confiance

Les tests unitaires sont un élément essentiel du développement logiciel. Ils vous permettent de vérifier que chaque unité de votre code (fonction, méthode, classe) fonctionne comme prévu, de manière isolée du reste du programme.

Ecrire des tests unitaires présente de nombreux avantages :

  • Fiabilité : les tests vous aident à détecter les erreurs tôt, avant qu'elles ne se propagent dans le reste du programme et ne causent des problèmes plus importants.
  • Maintenabilité : les tests vous permettent de modifier votre code en toute confiance, en vous assurant que vous n'introduisez pas de régressions (nouvelles erreurs).
  • Confiance : les tests vous donnent confiance dans la qualité de votre code. Si tous vos tests passent, vous pouvez être raisonnablement sûr que votre code fonctionne correctement.
  • Documentation : les tests servent de documentation vivante de votre code. Ils montrent comment utiliser chaque unité de code, et quels sont les résultats attendus.
  • Conception : l'écriture de tests vous force à réfléchir à la conception de votre code, et à le rendre plus modulaire et plus testable.

Nous verrons comment les tests unitaires s'intègrent dans le processus de développement logiciel, et comment ils contribuent à améliorer la qualité de votre code.

Le module `unittest` : le framework de test intégré de Python

Python fournit un module intégré pour les tests unitaires, appelé `unittest`. Ce module fournit un framework complet pour écrire et exécuter des tests.

Pour écrire des tests avec `unittest`, vous créez une classe qui hérite de la classe `unittest.TestCase`. Dans cette classe, vous définissez des méthodes de test, dont le nom commence par `test_`. Chaque méthode de test vérifie une fonctionnalité spécifique de votre code.

A l'intérieur des méthodes de test, vous utilisez des assertions pour vérifier que les résultats de votre code sont conformes à ce que vous attendez. `unittest` fournit de nombreuses méthodes d'assertion, comme `assertEqual(a, b)` (vérifie que `a` est égal à `b`), `assertTrue(x)` (vérifie que `x` est vrai), `assertFalse(x)` (vérifie que `x` est faux), `assertRaises(exception, fonction, *args, **kwargs)` (vérifie que l'appel de `fonction` avec les arguments donnés lève l'exception spécifiée), etc.

Vous pouvez organiser vos tests en suites de tests, et les exécuter avec un runner de tests (test runner). Le runner de tests exécute tous les tests, et affiche un rapport indiquant quels tests ont réussi et quels tests ont échoué.

Nous verrons comment utiliser le module `unittest`, comment créer une classe de test, comment définir des méthodes de test, comment utiliser des assertions, et comment exécuter les tests.

Ecrire des cas de test (assertions) : vérifiez le comportement de votre code

Un cas de test (test case) est un ensemble d'instructions qui vérifient une fonctionnalité spécifique de votre code. Il comprend généralement les étapes suivantes :

  1. Préparation : créer les objets et les données nécessaires pour le test.
  2. Action : appeler la fonction ou la méthode à tester, avec les arguments appropriés.
  3. Assertion : vérifier que le résultat de l'action est conforme à ce qui est attendu, en utilisant des assertions.

Il est important d'écrire des cas de test qui couvrent différents scénarios, y compris les cas limites (edge cases) et les cas d'erreur. Vous devez tester non seulement le comportement "normal" de votre code, mais aussi les situations où il pourrait échouer.

Pour écrire des assertions, vous utilisez les méthodes d'assertion fournies par `unittest` (ou par un autre framework de test). Chaque méthode d'assertion vérifie une condition spécifique, et lève une exception `AssertionError` si la condition n'est pas remplie.

Nous verrons des exemples de cas de test, comment écrire des assertions efficaces, et comment couvrir différents scénarios.

Mocks et patchs (`unittest.mock`) : isolez vos tests

Lorsque vous écrivez des tests unitaires, vous voulez tester chaque unité de code de manière isolée. Cependant, votre code peut dépendre d'autres unités de code (fonctions, méthodes, classes), ou de ressources externes (fichiers, bases de données, services web, etc.).

Pour isoler vos tests, vous pouvez utiliser des mocks (simulacres) et des patchs. Un mock est un objet qui simule le comportement d'un autre objet. Un patch est un mécanisme qui vous permet de remplacer temporairement une partie de votre code par un mock.

Le module `unittest.mock` fournit des outils pour créer des mocks et des patchs. Vous pouvez utiliser la classe `Mock` pour créer des objets mock, et la fonction `patch()` (ou le décorateur `@patch`) pour remplacer temporairement une partie de votre code par un mock.

Les mocks vous permettent de contrôler le comportement des dépendances de votre code, et de vérifier que votre code interagit correctement avec ces dépendances (par exemple, qu'il appelle les bonnes méthodes avec les bons arguments).

Nous verrons comment utiliser `unittest.mock`, comment créer des mocks, comment utiliser des patchs, et comment les utiliser pour isoler vos tests et les rendre plus fiables.

Exécuter les tests : automatisez la vérification

Une fois que vous avez écrit vos tests, vous devez les exécuter régulièrement, pour vous assurer que votre code fonctionne toujours comme prévu. Vous pouvez exécuter vos tests manuellement, mais il est préférable d'automatiser ce processus.

Vous pouvez exécuter vos tests avec le module `unittest` en utilisant la commande `python -m unittest` sur la ligne de commande. Vous pouvez spécifier le nom du fichier de test, le nom de la classe de test, ou le nom de la méthode de test à exécuter.

Vous pouvez également utiliser un runner de tests plus avancé, comme `pytest` (que nous verrons plus loin), qui offre des fonctionnalités supplémentaires (découverte automatique des tests, exécution en parallèle, rapports plus détaillés, etc.).

Il est important d'intégrer l'exécution des tests dans votre workflow de développement. Vous pouvez exécuter les tests automatiquement à chaque commit, ou avant chaque déploiement, en utilisant un outil d'intégration continue (CI) comme Jenkins, Travis CI, ou GitHub Actions.

Nous verrons comment exécuter les tests avec `unittest` et avec `pytest`, et comment intégrer l'exécution des tests dans votre workflow.

Utilisation de `pytest` (framework de test plus avancé) : simplifiez et enrichissez vos tests

`pytest` est un framework de test populaire pour Python, qui offre de nombreux avantages par rapport à `unittest` :

  • Simplicité : `pytest` est plus facile à utiliser que `unittest`. Vous n'avez pas besoin de créer des classes de test, vous pouvez simplement écrire des fonctions de test dont le nom commence par `test_`. Vous pouvez utiliser des assertions Python normales (`assert`) au lieu des méthodes d'assertion de `unittest`.
  • Découverte automatique des tests : `pytest` peut découvrir automatiquement les tests dans votre projet, sans que vous ayez besoin de les spécifier manuellement.
  • Exécution en parallèle : `pytest` peut exécuter vos tests en parallèle, ce qui peut réduire considérablement le temps d'exécution des tests.
  • Rapports détaillés : `pytest` fournit des rapports de test plus détaillés et plus lisibles que `unittest`.
  • Plugins : `pytest` dispose d'un écosystème de plugins qui étendent ses fonctionnalités (par exemple, pour générer des rapports de couverture de code, pour exécuter les tests dans un environnement isolé, pour intégrer les tests avec d'autres outils, etc.).

Nous verrons comment installer et utiliser `pytest`, comment écrire des tests avec `pytest`, comment exécuter les tests, et comment utiliser quelques-uns des plugins les plus utiles.

Débogage avec `pdb` (Python Debugger) : trouvez et corrigez les erreurs

Le débogage est le processus qui consiste à trouver et à corriger les erreurs dans votre code. Python fournit un débogueur intégré, appelé `pdb` (Python Debugger).

`pdb` vous permet d'exécuter votre code pas à pas, d'inspecter l'état de vos variables, d'évaluer des expressions, de définir des points d'arrêt (breakpoints), et d'autres actions qui vous aident à comprendre ce qui se passe dans votre programme.

Vous pouvez démarrer `pdb` de plusieurs manières :

  • En insérant `import pdb; pdb.set_trace()` dans votre code, à l'endroit où vous voulez commencer le débogage. Lorsque Python exécute cette ligne, il arrête l'exécution et ouvre une invite `pdb`.
  • En exécutant votre programme avec `python -m pdb mon_programme.py`. Cela démarre `pdb` dès le début de l'exécution.

Une fois que vous êtes dans `pdb`, vous pouvez utiliser des commandes pour contrôler l'exécution, inspecter les variables, etc. Voici quelques-unes des commandes les plus utiles :

  • `h` (help) : affiche l'aide.
  • `n` (next) : exécute la ligne suivante.
  • `s` (step) : exécute la ligne suivante, en entrant dans les fonctions appelées.
  • `c` (continue) : continue l'exécution jusqu'au prochain point d'arrêt.
  • `b` (break) : définit un point d'arrêt (vous pouvez spécifier le numéro de ligne, le nom de la fonction, etc.).
  • `p` (print) : affiche la valeur d'une variable ou d'une expression.
  • `l` (list) : affiche le code source autour de la ligne courante.
  • `q` (quit) : quitte le débogueur.

Nous verrons comment utiliser `pdb`, comment utiliser les commandes de base, et comment déboguer un programme simple.

Utilisation des outils de débogage des IDE : profitez d'interfaces graphiques

La plupart des environnements de développement intégrés (IDE) pour Python, comme VS Code, PyCharm, Spyder, etc., offrent des outils de débogage graphiques qui sont plus conviviaux que `pdb`.

Ces outils vous permettent de définir des points d'arrêt en cliquant dans la marge de l'éditeur, d'exécuter votre code pas à pas, d'inspecter les variables dans une fenêtre dédiée, d'évaluer des expressions, et d'autres actions, le tout avec une interface graphique.

Les outils de débogage des IDE sont généralement plus faciles à utiliser que `pdb`, et ils peuvent vous faire gagner beaucoup de temps lors du débogage.

Nous verrons comment utiliser les outils de débogage de VS Code et de PyCharm, deux IDE populaires pour Python.