Sommaire :
I. Qu’est-ce que pytest ?
II. Les différences entre pytest et unittest
III. Exemple simple de test de fonction avec pytest
IV. Organiser son dossier de test
V. Créer une fixture avec pytest
VI. Utiliser les fixtures avant et après les tests
Article précédent :
I. Qu'est-ce que Pytest ?
Pytest est un framework de test puissant et flexible pour Python. Il permet aux développeurs d'écrire des tests simples pour leurs applications.
Pytest est largement utilisé dans l'industrie pour tester du code pour du développement web mais aussi pour du Data Engineering ou du Machine Learning Engineering.
II. Les différences entre pytest et unittest
Bien que pytest et unittest soient tous deux des frameworks de test pour Python, ils présentent plusieurs différences qui font que, généralement, pytest est préféré à unittest :
Syntaxe et simplicité
Pytest offre une syntaxe plus concise et lisible que unittest. Les tests avec pytest nécessitent moins de code pour être lancés, ce qui les rend plus faciles à écrire et à comprendre. Pytest utilise des assertions pour ses tests (voir un exemple plus loin dans l’article).
Fixtures et plugins
Pytest propose un système de fixtures puissant pour la gestion des ressources de test et un large écosystème de plugins. Unittest offre un support limité pour les fixtures et ne dispose pas d’un système de plugins aussi étendu.
III. Exemple simple de test de fonction avec Pytest
Installez un Pytest d’abord :
uv add pytest
Et voici un exemple simple de test de fonction utilisant Pytest :
# Fichier : math_operations.py
def addition(a, b):
return a + b
# Fichier : test_math_operations.py
from math_operations import addition
def test_addition():
assert addition(2, 3) == 5
assert addition(-1, 1) == 0
assert addition(0, 0) == 0
Dans cet exemple, nous testons la fonction addition
simple. Voici comment fonctionne le test :
Nous importons la fonction
addition
du modulemath_operations
.Nous définissons une fonction de test nommée
test_addition
.À l'intérieur de la fonction de test, nous utilisons des assertions pour vérifier que la fonction
addition
renvoie les résultats attendus pour différentes entrées.
Pour exécuter ce test, il suffit d'installer pytest (uv add pytest
) et d'exécuter la commande pytest
dans le répertoire contenant le fichier de test.
Pytest découvrira automatiquement le fichier de test (car il commence par "test_") et exécutera les fonctions de test à l'intérieur.
IV. Organiser son dossier de test
Une bonne organisation des tests est cruciale pour maintenir un projet propre et facile à comprendre. Voici une structure recommandée pour organiser vos tests avec pytest :
projet/
│
├── src/
│ └── mon_module/
│ ├── __init__.py
│ └── fonctions.py
│
└── tests/
├── __init__.py
├── test_fonctions.py
└── conftest.py
Le dossier
src
contient le code source de votre projet.Le dossier
tests
contient tous vos fichiers de test.conftest.py
est un fichier spécial pour pytest où vous pouvez définir des fixtures partagées.Les fichiers de test sont préfixés par
test_
pour être automatiquement découverts par pytest.
V. Créer une fixture avec pytest
Les fixtures dans pytest sont des fonctions qui fournissent des données ou des objets réutilisables pour vos tests. Voici comment créer et utiliser une fixture :
import pytest
@pytest.fixture
def exemple_fixture():
return "Données de test"
def test_utilisant_fixture(exemple_fixture):
assert exemple_fixture == "Données de test"
Les fixtures peuvent être définies dans le même fichier que vos tests ou dans conftest.py
pour être partagées entre plusieurs fichiers de test.
Créer une fixture à partir d’un fichier
Il est possible dans le monde professionnel que vous vous basiez sur des fichiers CSV, JSON ou XML pour obtenir des données de tests. Ainsi, nous pouvons créer une fixture à partir d’un fichier de données.
Pour créer une fixture à partir d'un fichier, il est recommandé de placer ce fichier dans un sous-dossier dédié aux ressources de test. Voici un exemple de projet avec un dossier dédié :
projet/
├── src/
│ └── votre_code_source.py
├── tests/
│ ├── conftest.py
│ ├── test_votre_code.py
│ └── resources/
│ └── customer_data.json
Dans cette structure :
Placez votre fichier dans le dossier
tests/resources/
.Définissez votre fixture dans le fichier
conftest.py
.
Voici un exemple de comment vous pourriez définir votre fixture dans conftest.py
pour créer un objet Customer depuis un fichier json :
import pytest
import json
import os
@pytest.fixture
def customer_fixture():
file_path = os.path.join(os.path.dirname(__file__), 'resources', 'customer_data.json')
with open(file_path, 'r') as file:
data = json.load(file)
customer = Customer(name=data['name'], email=data['email'])
return customer
C’est un exemple mais on peut imaginer créer plusieurs customers d’un coup grâce à un fichier de données.
En plaçant le fichier dans un sous-dossier resources
, vous gardez vos fichiers de test et vos ressources de test bien organisés, ce qui facilite la maintenance à long terme de votre suite de tests.
VI. Utiliser les fixtures avant et après les tests
Très, très souvent vous voudrez effectuer des opérations spécifiques avant tous vos tests et après ceux-ci.
Voici un exemple de comment utiliser les fixtures pour ce cas-ci :
import pytest
@pytest.fixture(scope="module")
def setup_module():
print("Exécuté avant tous les tests du module")
yield
print("Exécuté après tous les tests du module")
def test_1(setup_module):
assert True
def test_2(setup_module):
assert True
La partie avant le
yield
est exécutée avant tous les tests.La partie après le
yield
est exécutée après tous les tests.scope="module"
assure que la fixture est exécutée une fois pour tout le module.
Il existe plusieurs valeurs possibles pour la variable scope :
function
: C'est le scope par défaut. La fixture est créée et détruite pour chaque fonction de test qui l'utilise. Cela assure une isolation maximale entre les tests.class
: La fixture est créée au début de la classe de test et détruite à la fin de la dernière méthode de test dans cette classe. Elle est partagée entre toutes les méthodes de test de la classe.module
: La fixture est créée une fois pour l'ensemble du module de test et détruite à la fin du module. Elle est partagée entre toutes les fonctions de test du module.package
: La fixture est créée au début du paquet de tests et détruite à la fin du dernier test du paquet, y compris les sous-paquets et sous-répertoires. Ce scope est considéré comme expérimental.session
: La fixture est créée une seule fois pour l'ensemble de la session de test et détruite à la fin de tous les tests. Elle est partagée entre tous les tests de la session, quel que soit le module ou le paquet.
Cet emploi des fixtures sert la plupart du temps à des connexions à des bases de données ou à d’autres services, et à ajouter des données de base, puis à faire le ménage ensuite à la fin des tests en fermant les connexions et en supprimant toutes les données ajoutées.
Nous verrons cette utilisation plus tard dans la formation mais pour vous donner une idée :
import pytest
@pytest.fixture(scope="function")
def db_session():
# Création de la connexion à la base de données
db(connexion_infos)
# Création d'un customer
new_customer = Customer(name="John Doe", email="john@example.com")
db.add(Customer, new_customer) # ajout à la table Customer de la bd
yield db # retourne l'objet db
# Suppression du client et nettoyage
db.delete(Customer, new_customer) # suppression du customer
db.close() # fermeture de la connexion
def test_client_creation(db_session):
customer = db.query(Customer).first() # requête la table Customer et retourne le premier élément
assert customer.name == "John Doe"
assert customer.email == "john@example.com"
Merci pour votre lecture !