Blog ENI : Toute la veille numérique !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
💥 Du 22 au 24 novembre : Accès 100% GRATUIT
à la Bibliothèque Numérique ENI. Je m'inscris !
  1. Livres et vidéos
  2. Selenium
  3. Les tests unitaires en Python
Extrait - Selenium Maîtrisez vos tests fonctionnels avec Python
Extraits du livre
Selenium Maîtrisez vos tests fonctionnels avec Python Revenir à la page d'achat du livre

Les tests unitaires en Python

Pourquoi les tests unitaires ?

Le lecteur peut être surpris de commencer son apprentissage par les tests unitaires avant de s’exercer aux tests fonctionnels. Malgré la différenciation de ces deux types de tests, ils n’en restent pas moins des tests. Ils se basent donc sur la même logique et les mêmes éléments de syntaxe.

Effectivement, en Python, un test fonctionnel s’écrit à travers la structure d’un test unitaire : quel que soit l’élément que nous testons, une fonction en test unitaire ou un comportement en test fonctionnel, nous posons une assertion à propos de cet élément et nous en vérifions la validité par la suite. Vous pouvez voir le principe d’assertion comme la condition d’une instruction conditionnelle, dans le sens où elle est soit vraie, i.e. le test est accepté, ou fausse, i.e. le test est rejeté.

Assertions

Nous pourrions penser qu’un test est une simple instruction conditionnelle if, car son résultat est un booléen, le test est validé ou non. Avec cette logique, l’écriture des tests serait rébarbative, non différenciée de l’écriture du code, et nécessiterait une convention interne de code pour bien organiser le code et les tests ainsi que leur validation. Il nous faudrait indiquer ce que nous devons afficher lorsqu’un test réussit, lorsqu’il échoue, indiquer un nouveau fichier principal pour lancer les tests, etc. Un réel bazar qui ne serait pas pratique donc pas maintenable.

Python intègre une instruction spécifique pour valider un test : assert.

assert test_conditionnel, [message] 

L’instruction assert est directement suivie par le test et peut contenir un message à afficher lorsque le test est rejeté. L’instruction représentant le test doit toujours pouvoir être interprétée comme un booléen.

Si le test est valide, i.e. une instruction retournant True, l’instruction assert laisse l’interpréteur Python continuer l’exécution du test. Dans le cas contraire, elle renvoie une exception AssertionError qui stoppe l’exécution du reste du script.

def divide(a, b):  
    assert b > 0, "Division par zéro...

Léger tour d’horizon des tests unitaires

Python étant un langage libre, ouvert et surtout avec une grande communauté très active, il existe une multitude de librairies de tests pour écrire des tests unitaires. Dans cette section, nous ne présenterons que les plus connues et/ou utilisées pour la rédaction de tests unitaires.

1. Script utilisé pour le comparatif

Afin de présenter les différentes librairies de tests en Python, nous allons implémenter un script représentant deux fonctions simples d’une calculette :

  • une division entre deux numériques ;

  • une soustraction entre deux numériques.

# Fonction de la division  
def divide(a, b):  
    return a / b  
   
# Fonction de la soustraction  
def substract(a, b):  
    return a - b 

2. Librairie Unittest

La librairie Unittest est historiquement la première librairie en Python permettant d’automatiser les tests unitaires.

Pour le lecteur connaissant le langage Java, cette librairie s’inspire de l’API JUnit.

La librairie Unittest est installée avec l’installation de Python, nous pouvons donc la voir comme la version native des tests unitaires de Python.

Pour écrire un test avec cette librairie, nous devons l’importer dans le script, puis créer une nouvelle classe héritant de TestCase et représentant tous les tests que nous voulons effectuer. La convention est de déclarer une méthode par test : chaque méthode n’aura donc qu’une seule instruction d’assertion. Cette convention nous permet de mieux comprendre les tests qui sont rejetés et donc de corriger notre code plus rapidement et surtout de manière plus cohérente. 

La librairie Unittest utilise ses propres instructions d’assertion, dont :

  • La méthode assertEqual(paramètre1, paramètre2) testant que le premier paramètre retourne bien la valeur du deuxième paramètre.

  • La méthode assertTrue(condition) qui lance une exception lorsque la condition est fausse.

  • La méthode assertFalse(condition) qui est l’inverse de la méthode assertTrue.

Le lancement des tests de la classe se fait par l’appel de l’instruction unittest.main().

import unittest  
import...

À l’assaut de pytest

Comme nous l’avons vu dans la section précédente, Pytest possède de nombreux avantages pour l’écriture et l’exécution des tests unitaires en Python :

  • programmer avec des fonctions ou avec des classes et des objets, au choix du développeur ;

  • un test = une fonction ou une méthode = une assertion ;

  • l’utilisation de l’instruction native de Python assert ;

  • un rapport de test détaillé et clair ;

  • l’exécution avec la ligne de commande pytest, et non python (ce point facilite énormément l’exécution des tests lors des mécanismes d’intégration continue (CI) dans la philosophie DevOps).

Pytest permet également :

  • de tester la levée d’exception ;

  • d’éviter les répétitions avec le décorateur fixture ;

  • de lancer plusieurs fois le même test avec différentes valeurs avec le décorateur parametrize.

Un décorateur est un patron de conception (design pattern) permettant d’affecter des comportements spécifiques à une classe ou à une fonction/méthode sans implémenter ses comportements à chaque fois, juste en les indiquant avec @ en Python.

Transformons nos fonctions de calcul en une classe Calculette, avec une division permettant d’illustrer les dernières spécificités de pytest.

class Calculette :  
   
    def __init__(self):  
          # Au départ, aucun résultat n'étant calculé => 
Not A Number  
         ...

Logger pour mieux comprendre

1. Intérêt des logs

Dans un projet informatique, les logs sont généralement utilisés en production pour pouvoir retracer les instructions exécutées, et notamment les rejouer en cas d’exceptions ou d’erreurs de logique découvertes inopinément par un utilisateur.

Les logs sont des traces écrites par le script dans un fichier dédié. Ces traces sont entièrement décidées par les développeurs : nous choisissons quoi écrire, comment l’écrire, où l’écrire, etc.

Les logs sont également un élément intéressant pour garder une trace des tests exécutés et ainsi pouvoir revenir sur nos jeux de test en cas de problème découvert de notre application.

2. Écrire des logs avec Python

a. Module logging

Le module logging est intégré dans la distribution standard de Python, il n’est donc nullement besoin de l’installer.

Une fois qu’il est importé, une bonne pratique est de stocker les logs dans un fichier avec l’extension .log sur le système. Pour ce faire, nous utilisons la méthode basicConfig qui permet de configurer les logs, comme son nom l’indique.

import logging  
  
logging.basicConfig(filename='example.log', encoding='utf-8')...

Exercices

1. Exercice 1

Écrivez un script Python avec deux fonctions : une pour calculer le prix TTC d’un numérique passé en paramètre et l’autre pour calculer le prix HT d’un numérique passé en paramètre. Nous fixons le taux de TVA à 20 %. Écrivez ensuite le test à l’aide de Pytest pour tester vos fonctions.

2. Exercice 2

Transformez le script de l’exercice précédent en classe. Pensez à modifier votre test à l’aide de Pytest pour y intégrer cette classe en fixture.

3. Exercice 3

Écrivez une classe Cercle en Python, avec le calcul de sa surface et de son périmètre. Tester par la suite ces deux calculs dans un script de test à l’aide de Pytest.

4. Exercice 4

Écrivez les tests à l’aide de Pytest qui couvrent l’ensemble des traitements possibles pour un script permettant de jouer au morpion implémenté sans la programmation orientée objet.

Les tests unitaires ne couvrent pas les saisies clavier, ils couvrent uniquement les tests des traitements de données, c’est-à-dire l’appel de fonctions qui ne nécessite pas une action de l’utilisateur. Les saisies clavier relèvent des tests fonctionnels.

5. Exercice 5

Écrivez les tests à l’aide de Pytest qui couvrent l’ensemble des traitements possibles...