Couvrir plusieurs cas d’entrée
Objectif : Tester les variations du calcul du total des dépenses sans dupliquer les mêmes vérifications.
Un calcul métier peut fonctionner pour une dépense courante et échouer lorsque la liste est vide ou contient plusieurs éléments. Ajouter un test distinct pour chaque situation répète souvent la même préparation et la même assertion. Vous allez regrouper ces variations dans un test paramétré et rendre explicites les entrées que le service doit traiter.
La leçon précédente a établi la structure Arrange-Act-Assert pour tester une règle métier isolément. Ici, vous conservez cette structure, mais vous fournissez plusieurs jeux de données au même test. L’exemple porte sur calculer_total et le modèle Depense du paquet depenses ; il suppose que le constructeur accepte une description et un montant, et que les montants sont des nombres décimaux représentables exactement comme des valeurs usuelles telles que 12.5.
Paramétrer un test du total
Avec pytest.mark.parametrize, vous déclarez les cas dans une table de paramètres. Pytest exécute ensuite la même fonction une fois par ligne : chaque cas reste identifiable, mais le corps de test n’est écrit qu’une seule fois. Le fichier ci-dessous remplace le test unitaire de tests/test_service.py par une série de vérifications ciblées.
import pytest
from depenses.modeles import Depense
from depenses.service import calculer_total
@pytest.mark.parametrize(
("depenses", "total_attendu"),
[
pytest.param([], 0, id="liste-vide"),
pytest.param([Depense("Café", 2.5)], 2.5, id="une-depense"),
pytest.param(
[Depense("Repas", 12.5), Depense("Transport", 3.0)],
15.5,
id="plusieurs-depenses",
),
pytest.param(
[Depense("Fournitures", 0.0), Depense("Repas", 12.5)],
12.5,
id="montant-nul",
),
],
)
def test_calculer_total(depenses: list[Depense], total_attendu: float) -> None:
total = calculer_total(depenses)
assert total == total_attenduLes quatre cas vérifient des comportements différents : l’absence de dépense, une seule dépense, l’addition de plusieurs montants et la présence d’un montant nul. Les identifiants donnés à pytest.param apparaissent dans les résultats de test et facilitent le diagnostic. Par exemple, un échec sur plusieurs-depenses désigne immédiatement le cas où l’addition doit produire 15.5.
Lancer les cas et lire les résultats
Depuis la racine du projet, lancez d’abord le fichier concerné. Cette commande exécute chaque paramètre comme un cas indépendant ; si les quatre assertions réussissent, pytest affiche quatre tests réussis. Vous pouvez ensuite exécuter toute la suite afin de vérifier que l’évolution ne perturbe pas d’autres composants.
python -m pytest tests/test_service.py -v
python -m pytesttests/test_service.py::test_calculer_total[liste-vide] PASSED
tests/test_service.py::test_calculer_total[une-depense] PASSED
tests/test_service.py::test_calculer_total[plusieurs-depenses] PASSED
tests/test_service.py::test_calculer_total[montant-nul] PASSED
4 passedCas limites et entrées invalides
Un cas limite se trouve à la frontière des entrées attendues : une liste vide ou un montant nul, par exemple. Une entrée invalide enfreint une règle du domaine, telle qu’un montant négatif si le contrat du service l’interdit. N’ajoutez un test d’entrée invalide qu’après avoir confirmé le comportement attendu dans le code ou les exigences ; vous pourriez alors paramétrer les valeurs invalides et vérifier l’exception prévue, sans inventer une règle ni imposer un type d’exception arbitraire.
Évitez d’accumuler des cas redondants : choisissez des entrées qui couvrent des comportements distincts, puis gardez les résultats attendus lisibles. Si un paramètre échoue, partez de son identifiant et comparez le résultat réel avec le contrat métier. Pour une anomalie confirmée, corrigez le service et relancez les tests ; ne changez pas l’attendu simplement pour obtenir une suite verte.
À retenir
- Utilisez pytest.mark.parametrize pour vérifier plusieurs jeux de données avec un seul corps de test.
- Incluez des cas distincts : entrée vide, élément unique, plusieurs éléments et valeurs frontières pertinentes.
- N’écrivez un test de valeur invalide qu’en fonction d’une règle métier et d’un comportement d’erreur définis.
- Exécutez d’abord le test ciblé, puis l’ensemble de la suite, et exploitez les identifiants de cas pour diagnostiquer les échecs.