Les tests unitaires modernes avec Pytest

@joekakone

Joseph Konka @joekakone

03/03/2026 · 10 min

Découvrez le framework de test de référence dans l'industrie logicielle : Pytest. Écrivez des tests concis avec le mot-clé assert, injectez des dépendances avec les fixtures et paramétrez vos tests avec @pytest.mark.parametrize.

Les tests unitaires modernes avec Pytest#

Bien que le module natif unittest soit très complet, l'écosystème professionnel Python plébiscite massivement pytest comme le standard absolu de l'industrie.

Pourquoi pytest s'est-il imposé ? 1. Zéro cérémonial (Boilerplate minimal) : pas besoin de créer des classes complexes héritant de TestCase. De simples fonctions Python autonomes préfixées par test_ suffisent. 2. L'instruction native assert : fini les dizaines de méthodes self.assertEqual(), self.assertIn(). Vous utilisez l'instruction Python ordinaire assert condition. 3. Introspection détaillée des échecs : lorsqu'un test échoue, pytest dissèque l'expression et affiche exactement les valeurs des variables en jeu. 4. Le système révolutionnaire de Fixtures (@pytest.fixture) et la paramétrisation (@pytest.mark.parametrize).


1. Installation et premier test avec pytest#

Pytest s'installe via le gestionnaire de paquets officiel pip :

pip install pytest

Écriture d'un test minimal#

test_math_simple.py
def carre(x: int) -> int:
    return x * x

def test_carre_nombre_positif():
    assert carre(4) == 16

def test_carre_nombre_negatif():
    assert carre(-3) == 9

def test_carre_zero():
    assert carre(0) == 0

Pour exécuter les tests, tapez simplement dans votre terminal :

pytest -v


2. Tester les exceptions : pytest.raises()#

Pour vérifier qu'une fonction lève l'exception attendue face à des entrées invalides :

import pytest

def diviser(a: float, b: float) -> float:
    if b == 0:
        raise ZeroDivisionError("Le denominateur ne peut pas etre nul !")
    return a / b

def test_division_par_zero():
    # Vérifie que l'erreur ZeroDivisionError est bien déclenchée
    with pytest.raises(ZeroDivisionError) as info_erreur:
        diviser(10, 0)

    # On peut même vérifier le message de l'exception
    assert "denominateur" in str(info_erreur.value)

3. Les Fixtures de test : @pytest.fixture#

Une fixture est une fonction décorée qui prépare un environnement ou un jeu de données (connexion à une base de test, objet initialisé, fichier temporaire) et l'injecte automatiquement en paramètre des fonctions de test qui la demandent :

import pytest

class PanierAchat:
    def __init__(self):
        self.articles = []

    def ajouter(self, nom: str, prix: float):
        self.articles.append({"nom": nom, "prix": prix})

    @property
    def total(self) -> float:
        return sum(item["prix"] for item in self.articles)


# Définition de la Fixture
@pytest.fixture
def panier_garni():
    """Prépare un panier contenant déjà deux articles de référence."""
    panier = PanierAchat()
    panier.ajouter("Livre Python", 29.90)
    panier.ajouter("Stylo", 2.10)
    return panier


# Utilisation de la fixture : elle est passée comme argument du test !
def test_total_panier_initial(panier_garni):
    assert panier_garni.total == 32.00

def test_ajout_article_augmente_total(panier_garni):
    panier_garni.ajouter("Clavier", 48.00)
    assert panier_garni.total == 80.00
    assert len(panier_garni.articles) == 3

4. Paramétrisation de tests : @pytest.mark.parametrize#

Plutôt que d'écrire dix fonctions de test presque identiques pour tester différents cas limites, pytest permet d'exécuter un test unique sur une matrice complète d'entrées et de sorties :

import pytest

def est_palindrome(chaine: str) -> bool:
    s = chaine.lower().replace(" ", "")
    return s == s[::-1]

# Exécute 5 tests unitaires distincts en 4 lignes de code !
@pytest.mark.parametrize("texte, attendu", [
    ("radar", True),
    ("kayak", True),
    ("Python", False),
    ("Elu par cette crapule", True),
    ("", True),
])
def test_palindromes(texte, attendu):
    assert est_palindrome(texte) == attendu

5. Synthèse comparative : unittest vs pytest#

Fonctionnalité unittest (Standard) pytest (Standard Industrie)
Installation Aucune (Inclus avec Python) pip install pytest
Structure du code Classes obligatoires (TestCase) Fonctions légères et expressives
Syntaxe d'assertion Verbeuse (self.assertEqual(a, b)) Naturelle et concise (assert a == b)
Préparation (Fixtures) setUp() / tearDown() rigides Fixtures modulaires injectables (@pytest.fixture)
Tests paramétrés Complexe ou via sous-tests Élégant (@pytest.mark.parametrize)
Rapports d'erreurs Basiques Visuels avec valeurs des variables en panne

6. Défi d'application#

À l'aide de @pytest.mark.parametrize, écrivez une suite de tests unitaires pour une fonction valider_code_postal(code: str) -> bool qui vérifie qu'un code postal français comporte exactement 5 chiffres numériques (ex: "75001" \(\rightarrow\) True, "7500" \(\rightarrow\) False, "7500A" \(\rightarrow\) False, "" \(\rightarrow\) False).

Commentaires

Inscrivez-vous à une formation complète

Voir le catalogue