Test unitaire - Écrire, lire et juger un test efficace (Exemple Python)

Denis Ribeiro .

1 avril 2026

Exemple de test unitaire en Python dans PyCharm. Le code vérifie le bon fonctionnement de la fonction `getVerkaufpreis`.

Un bon test unitaire ne sert pas seulement à vérifier qu’un code “marche”. Il protège une règle métier précise, sur une entrée connue, avec une sortie attendue et sans dépendre de la base de données, du réseau ou de l’interface. Ici, je prends un exemple concret et réaliste pour montrer comment écrire, lire et juger un test unitaire sans tomber dans un modèle trop académique.

Les points essentiels à garder en tête avant de regarder l’exemple

  • Un test unitaire valide une seule logique locale, pas tout le système.
  • Le test doit être rapide, déterministe et facile à relire.
  • L’approche la plus claire suit souvent le trio préparer, agir, vérifier.
  • Un bon exemple teste aussi les cas limites et les erreurs attendues.
  • Si une dépendance externe gêne la lecture, je l’isole avec un mock ou un double de test.

Pyramide des tests : tests unitaires (isolés), tests d'intégration (groupés), tests d'acceptation (application complète). Un exemple de test unitaire est la base.

Ce que je veux montrer avec un vrai exemple

Quand je parle de test unitaire, je pense d’abord à une fonction pure ou à une méthode qui prend des données en entrée et retourne un résultat précis. C’est le terrain idéal pour apprendre, parce qu’on voit immédiatement ce qui est vérifié et pourquoi le test échoue.

Pour rendre l’idée utile, je pars d’une fonction de remise commerciale. Elle n’a rien d’exotique, mais elle ressemble à ce qu’on trouve souvent dans un backend, une API ou un service métier. C’est exactement le type de logique qui mérite d’être testé tôt, parce qu’une erreur de calcul se propage très vite dans toute l’application.

La documentation Python insiste d’ailleurs sur un point que je retrouve dans tous les projets sérieux: un cas de test doit rester autonome et exécutable isolément. C’est ce qui fait la différence entre un vrai test unitaire et un scénario flou qui dépend du reste du système.

Ce cadre étant posé, je peux montrer un exemple simple en Python avant de le décortiquer. C’est la façon la plus directe de comprendre la mécanique.

Un exemple concret de test unitaire en Python

Voici une fonction qui calcule une remise en pourcentage, puis deux tests unitaires qui vérifient son comportement normal et son comportement en cas d’erreur.

# remise.py
def calculer_remise(montant_ht, pourcentage):
    if montant_ht < 0:
        raise ValueError("Le montant doit être positif")
    if not 0 <= pourcentage <= 100:
        raise ValueError("Le pourcentage doit être compris entre 0 et 100")
    return round(montant_ht * (1 - pourcentage / 100), 2)
# test_remise.py
import pytest
from remise import calculer_remise

def test_calculer_remise_standard():
    # arrange
    montant = 100
    taux = 20

    # act
    resultat = calculer_remise(montant, taux)

    # assert
    assert resultat == 80.0

def test_calculer_remise_refuse_un_pourcentage_invalide():
    with pytest.raises(ValueError):
        calculer_remise(100, 140)

Ce petit exemple fait déjà l’essentiel du travail. Il montre une règle métier claire, il vérifie une valeur attendue, et il contrôle aussi une erreur de validation. C’est beaucoup plus utile qu’un test qui se contente de répéter le code de production sous une autre forme.

Je préfère ce genre d’exemple parce qu’il reste lisible en quelques secondes. Quand un test demande trop d’explications, c’est souvent le signal qu’il teste trop de choses à la fois ou qu’il s’appuie sur des dépendances mal isolées. C’est justement ce que je détaille maintenant.

Lire ce test ligne par ligne

Je résume souvent un test unitaire par un triptyque simple: préparer, agir, vérifier. En anglais, on parle d’AAA pour Arrange, Act, Assert. Ce n’est pas une formule magique, mais c’est un repère pratique pour éviter les tests confus.

Préparer les données

Dans le premier test, je fixe un montant à 100 et une remise à 20. Cette préparation doit être minimale. Plus il y a de variables inutiles, plus le test devient difficile à comprendre et à maintenir.

Exécuter une seule action

Je lance ensuite calculer_remise(montant, taux). Un bon test unitaire ne devrait pas multiplier les appels ni simuler un parcours complet. Il cible une seule décision métier, une seule branche logique, une seule réponse attendue.

Lire aussi : Puissance en C - Évitez les pièges de `pow()` et du `^`

Vérifier un résultat précis

Enfin, j’utilise une assertion unique et explicite: le résultat doit valoir 80.0. Cette partie est le cœur du test. Si l’assertion est floue, le test ne dit plus grand-chose. Si elle est trop large, elle peut laisser passer des régressions.

Le second test vérifie l’exception. Là aussi, l’idée est simple: si le pourcentage dépasse 100, la fonction doit refuser l’entrée. Ce n’est pas un détail technique, c’est une règle métier. Dans les projets un peu sérieux, ce genre de garde-fou évite des erreurs bien plus coûteuses que le test lui-même.

Je trouve que c’est souvent à ce moment-là que les débutants comprennent vraiment l’intérêt du test unitaire: ce n’est pas un script qui “passe”, c’est une petite preuve automatisée qu’une règle précise tient encore. Et cette preuve n’a de valeur que si le test couvre aussi son périmètre réel.

Ce que ce test couvre vraiment

Un test unitaire ne remplace pas tous les autres types de vérification. Il couvre un morceau de logique isolé, pas l’ensemble du système. Pour clarifier ce point, je compare souvent les trois niveaux les plus courants.

Type de test Portée Vitesse Ce que je vérifie en priorité
Test unitaire Une fonction, une méthode, une classe Généralement en millisecondes La logique locale et les cas limites
Test d’intégration Plusieurs composants ensemble Souvent plus lent Les échanges entre modules, base, API, filesystem
Test de bout en bout Le parcours utilisateur complet Le plus lent Le comportement global du produit

En pratique, je garde les tests unitaires pour les règles métier, les calculs, les validations et les transformations de données. Dès qu’une dépendance externe entre en jeu, je me pose une autre question: est-ce encore un test unitaire, ou est-ce déjà un test d’intégration déguisé ? La réponse change souvent la manière d’écrire le test.

Cette distinction est importante parce qu’elle évite une erreur fréquente: vouloir faire porter à un test unitaire le poids d’un scénario complet. C’est là que la suite devient lente, fragile et difficile à diagnostiquer. La suite suivante revient justement sur ces pièges.

Les erreurs que je vois le plus souvent

Le premier piège, c’est de tester trop large. Un test qui prépare une base de données, appelle une API et vérifie l’interface graphique n’est plus un test unitaire. Il peut être utile, mais il n’a plus la même fonction.

Le deuxième piège, c’est l’assertion molle. Vérifier seulement qu’“aucune erreur n’est levée” ne suffit pas. Je veux savoir ce qui doit être vrai, pas seulement ce qui ne s’est pas cassé.

Le troisième piège, c’est de dépendre du hasard. Une date courante, un ordre de liste non garanti, un appel réseau ou un état partagé rendent le test instable. Un test instable finit presque toujours par être ignoré, puis supprimé, ce qui annule tout son intérêt.

Le quatrième piège, c’est de dupliquer la logique de production dans le test. Si le test fait exactement le même calcul que le code qu’il prétend vérifier, il ne détecte plus grand-chose. Il devient une copie décorative, pas un filet de sécurité.

Le cinquième piège, enfin, c’est la dépendance mal isolée. Quand une fonction appelle un service externe, je préfère souvent un mock, c’est-à-dire un faux objet qui simule la dépendance. Cela me permet de tester le comportement attendu sans payer le coût ni l’aléa du service réel.

Quand ces erreurs disparaissent, la suite devient plus courte, plus nette et bien plus fiable. C’est aussi pour cela que l’on peut réutiliser la même logique dans d’autres langages sans réinventer la méthode à chaque fois.

Adapter la logique à JavaScript ou Java

Le langage change, mais l’idée reste identique. En JavaScript, j’écrirai souvent un test avec expect dans Jest ou Vitest. En Java, je retrouverai la même structure avec JUnit et des assertions équivalentes.

Par exemple, la vérification du calcul reste conceptuellement la même:

expect(calculerRemise(100, 20)).toBe(80);
assertEquals(80.0, calculerRemise(100, 20));

La syntaxe varie, mais la logique ne bouge pas: une entrée, une action, une sortie attendue. C’est pour cela qu’un bon exemple de test unitaire est si pédagogique. Il apprend une méthode, pas seulement une API de framework.

Dans les projets JavaScript modernes, je conseille généralement de garder les tests unitaires au plus près de la logique pure. Dans les projets Java, le même principe s’applique, que l’on utilise JUnit seul ou en complément d’outils de mock comme Mockito. Ce qui compte, ce n’est pas le nom de l’outil, mais la netteté de l’isolement.

Une fois cette base maîtrisée, il devient beaucoup plus simple de structurer une vraie stratégie de test sans empiler des scénarios inutiles.

Ce que je retiens avant d’écrire ma propre suite

Si je devais résumer l’approche en une règle simple, je dirais ceci: je teste d’abord la logique la plus stable, la plus locale et la plus rentable à protéger. Une fonction de calcul, une validation de formulaire, une transformation de données ou une règle de décision sont de très bons candidats.
  • Je commence par un cas nominal simple, puis j’ajoute un cas limite.
  • Je garde chaque test court et explicite.
  • J’évite de faire dépendre le test d’un système externe quand ce n’est pas nécessaire.
  • Je préfère une suite de tests faciles à relire à une suite “complète” mais opaque.

Ce type d’exemple n’est pas spectaculaire, et c’est justement sa force. Il montre comment écrire un test qui protège réellement le code, sans bruit inutile ni dépendances parasites. Si je veux progresser vite, je pars de ce niveau de clarté, puis j’élargis seulement quand la logique du projet l’exige.

Questions fréquentes

Un test unitaire valide une logique locale, comme une fonction ou une méthode, avec des entrées connues et des sorties attendues. Il assure la protection d'une règle métier précise sans dépendre de facteurs externes (base de données, réseau).
L'isolation, souvent via des mocks, garantit que le test reste rapide, déterministe et ne soit pas affecté par des éléments extérieurs. Cela permet de se concentrer uniquement sur la logique testée, évitant ainsi les tests lents et fragiles.
La structure "Arrange, Act, Assert" (Préparer, Agir, Vérifier) est idéale. Elle consiste à préparer les données, exécuter l'action à tester, puis vérifier le résultat précis. Cela rend le test clair, lisible et facile à maintenir.
Non, un test unitaire se concentre sur une logique isolée et ses cas limites. Il ne remplace pas les tests d'intégration ou de bout en bout qui couvrent des interactions plus larges du système. La clé est de tester ce qui est le plus stable et rentable à protéger.
Oui, les concepts fondamentaux (isolation, "Arrange, Act, Assert", vérification de la logique métier) sont universels. La syntaxe et les outils (pytest en Python, Jest en JavaScript, JUnit en Java) peuvent varier, mais la méthode reste la même.

Évaluer l'article

Moyenne: 0.0 / 5 · 0 évaluations

Tags

test unitaire exemple test unitaire python exemple concret comment écrire un bon test unitaire
Autor Denis Ribeiro
Denis Ribeiro
Je m'appelle Denis Ribeiro et je suis passionné par les technologies, en particulier dans les domaines du web, de l'intelligence artificielle, des réseaux et de la sécurité. Fort de plusieurs années d'expérience en tant qu'analyste de l'industrie, j'ai eu l'occasion d'explorer en profondeur ces sujets, en me concentrant sur les évolutions et les tendances qui façonnent notre monde numérique. Mon expertise me permet d'analyser des données complexes et de les présenter de manière accessible, afin que chacun puisse comprendre les enjeux technologiques actuels. Je m'efforce d'apporter une perspective objective et factuelle à mes écrits, en vérifiant rigoureusement les informations pour garantir leur fiabilité. Je suis engagé à fournir à mes lecteurs des contenus précis, à jour et impartiaux, car je crois fermement que l'accès à une information de qualité est essentiel pour naviguer dans l'univers technologique en constante évolution. Mon objectif est de contribuer à une meilleure compréhension des défis et des opportunités que présente le monde numérique.

Commentaires (0)

Ajouter un commentaire