Aller au contenu

Numérique et sciences informatiques (spécialité) · Première

Spécification et tests en Python : cours de NSI en Première

Publié le 6 novembre 2026 · Mis à jour le 6 novembre 2026 · Signaler une erreur

L'essentiel

  • Une spécification est l'ensemble des exigences que doit satisfaire un produit, donc ici une fonction : ce qu'elle reçoit, ce qu'elle renvoie 4.
  • Un test confronte une réalisation à sa spécification : à une entrée donnée correspond une sortie attendue 5.
  • Un test unitaire contrôle une partie précise d'un programme, par exemple une fonction, séparément du reste 5.
  • Un jeu de test réussi ne prouve pas que le code est juste : il faut choisir des cas variés, dont les cas limites.
  • Écrire les tests avant le code évite de se laisser influencer par ce que le code fait déjà 5.

Pourquoi spécifier et tester ?

Tu as déjà écrit des fonctions qui « marchaient » sur ton exemple, puis qui plantaient dès qu'on leur donnait autre chose. Ce chapitre t'apprend à éviter ce piège avec deux outils complémentaires : la spécification, qui dit précisément ce que la fonction doit faire, et les tests, qui vérifient qu'elle le fait vraiment.

En NSI, ces deux notions reviennent partout : dans les exercices de programmation, dans les projets et dans l'épreuve pratique. Le premier exercice de cette épreuve te fait programmer un algorithme du programme, pour lequel une spécification t'est donnée 2. Savoir lire une spécification, c'est donc savoir ce qu'on attend de toi.

Le raisonnement se déroule en plusieurs temps :

  1. Avant d'écrire le code, tu décris ce que la fonction doit faire (la spécification).
  2. Tu écris le code qui réalise cette description.
  3. Tu vérifies par des tests que le code respecte la description.

Si tu sautes la première étape, tu ne sais pas ce que tu testes. Si tu sautes la dernière, tu ne sais pas si ton code est correct.

La démarche : spécifier, tester, coder, vérifierÉtapes successives : spécifier la fonction, écrire les tests, écrire le code, relancer les tests pour vérifier.1SpécifierEntrées, précondition, postcondition2TesterÉcrire les assert avant le code3CoderÉcrire la fonction demandée4VérifierRelancer les tests et corriger
  1. SpécifierEntrées, précondition, postcondition
  2. TesterÉcrire les assert avant le code
  3. CoderÉcrire la fonction demandée
  4. VérifierRelancer les tests et corriger
La démarche : spécifier, tester, coder, vérifier · Les tests s'écrivent à partir de la spécification, avant le code.

Qu'est-ce qu'une spécification ?

Dans le monde professionnel, la spécification n'apparaît pas au hasard. Une étude préalable décrit d'abord l'existant et les attentes des utilisateurs, puis la spécification reprend ces attentes pour les détailler 4. Autrement dit, on part d'un besoin vague (« un logiciel qui gère des notes ») pour aller vers des exigences précises (« la fonction renvoie la moyenne d'une liste de notes »).

La spécification fonctionnelle décrit les tâches que le produit assure et la façon dont il dialogue avec les utilisateurs ou avec d'autres produits 4. À notre échelle, c'est le même esprit : on décrit ce que fait la fonction, pas comment elle le fait.

Écrire la spécification dans le code Python

En Python, on place la spécification dans la chaîne de documentation (la docstring), écrite juste sous la ligne def, entre triples guillemets. Elle sert de contrat entre la personne qui écrit la fonction et celle qui l'utilise.

Les annotations de type donnent une version compacte de la spécification des types :

def moyenne(t: list[float]) -> float:
    ...

Elles documentent le programme, mais Python ne les impose pas à l'exécution. Elles ne remplacent donc ni la docstring (qui exprime la précondition et la postcondition) ni les tests.

Qu'est-ce qu'un test ?

Concrètement, un test se compose de trois éléments :

  • une entrée (les arguments donnés à la fonction) ;
  • une sortie attendue, déduite de la spécification et non du code ;
  • une comparaison entre la sortie obtenue et la sortie attendue.

Si les deux valeurs sont égales, le test réussit. Sinon il échoue, et tu sais qu'il existe un défaut, soit dans le code, soit dans ton test, soit dans la spécification.

Le test unitaire

L'intérêt est de tester chaque morceau séparément. La personne qui programme examine un module sans tenir compte du reste, afin de s'assurer qu'il répond aux spécifications fonctionnelles et se comporte bien quelles que soient les circonstances 5. Cette vérification est jugée indispensable surtout pour les applications critiques 5.

L'avantage pratique est immédiat : quand un programme de 200 lignes plante, tu ne sais pas où chercher. Si chaque fonction a été testée séparément, tu sais que l'erreur se trouve sans doute dans l'assemblage, ou dans la fonction dont les tests étaient trop pauvres.

Dans les langages courants, il existe de nombreux cadriciels (frameworks) qui facilitent l'écriture des tests unitaires 5. En NSI, tu peux te contenter d'assert et d'une fonction de test qui regroupe plusieurs vérifications :

def test_maximum():
    assert maximum([3, 8, 5]) == 8
    assert maximum([4]) == 4
    assert maximum([-5, -2, -9]) == -2
    assert maximum([7, 7, 7]) == 7
    print("maximum : tous les tests passent")

test_maximum()

Choisir un bon jeu de test

Réussir cinq tests ne prouve pas que ton code est juste. Un test qui échoue montre qu'une erreur existe ; un test qui réussit établit seulement que le code est correct pour cette entrée. La qualité d'un jeu de test dépend donc de la variété des cas choisis.

Tester d'abord, coder ensuite

Les tests doivent refléter la spécification. Mieux vaut donc les rédiger en premier, puis faire en sorte que le code les satisfasse, plutôt que de coder d'abord et de risquer que le code oriente la rédaction des tests 5. Cette démarche est défendue par les partisans de la méthode TDD (Test Driven Development) 5.

La première étape du modèle proposé consiste à écrire une fonction de test qui attend un résultat fixé par la spécification. Comme elle appelle du code encore inexistant, elle échoue forcément ; elle sert alors à définir ce que l'on teste 5.

Vérifier la précondition dans le code

On peut aussi utiliser assert pour contrôler les préconditions au début d'une fonction. Cela transforme une erreur silencieuse en erreur explicite, plus facile à comprendre.

def moyenne(t):
    """Renvoie la moyenne des éléments du tableau t (non vide)."""
    assert len(t) > 0, "le tableau ne doit pas être vide"
    return sum(t) / len(t)

Si quelqu'un appelle moyenne([]), le message indique clairement quelle exigence n'est pas respectée.

Les tests dans l'épreuve pratique et la méthode complète

Dans le premier exercice de l'épreuve pratique, tu programmes un algorithme nommé au programme et sans difficulté particulière, à partir d'une spécification qui t'est donnée 2. L'énoncé peut joindre des exemples avec leurs résultats attendus 2. Une autre forme consiste à compléter un programme à trous afin qu'il réponde à la spécification 2.

Dans tous les cas, la méthode est la même : lire la spécification jusqu'au bout, repérer les entrées et la sortie, regarder les exemples du jeu de test, puis écrire le code et le tester.

Erreurs fréquentes et limites des tests

Exercices corrigés

Exercice 1 Spécifier et tester une somme de nombres pairs

★☆☆Environ 10 min

On veut une fonction somme_pairs(n) qui reçoit un entier n positif ou nul et renvoie la somme de tous les entiers pairs compris entre 00 et nn (bornes incluses).

  1. Écris la docstring de spécification (précondition et postcondition).
  2. Propose trois assert dont un cas limite.
  3. Quelle valeur la fonction doit-elle renvoyer pour n = 10 ?
Indice 1

Les entiers pairs entre 0 et 10 sont 0, 2, 4, 6, 8, 10.

Indice 2

Le cas limite le plus petit est n = 0.

Voir la correction pas à pas
  1. Docstring : « Renvoie la somme des entiers pairs de 0 à n inclus. Précondition : n est un entier supérieur ou égal à 0. Postcondition : le résultat est la somme de tous les k pairs tels que 0 <= k <= n. »

  2. Tests : assert somme_pairs(0) == 0 (cas limite, seul 0 est pair), assert somme_pairs(1) == 0 (1 est impair, 0 est le seul pair) et assert somme_pairs(4) == 6 (0 + 2 + 4).

  3. Pour n = 10, on additionne 0 + 2 + 4 + 6 + 8 + 10 = 30. La fonction doit donc renvoyer 30.

La sortie attendue se calcule à la main d'après la spécification, sans écrire le code. Le cas n = 0 montre qu'il faut décider si la fonction accepte la borne inférieure : la spécification l'autorise, donc on la teste.

Exercice 2 Trouver le test qui révèle le défaut

★★☆Environ 15 min

Voici une fonction censée renvoyer le plus grand élément d'un tableau non vide de nombres :

def maximum(t):
    m = 0
    for x in t:
        if x > m:
            m = x
    return m
  1. Les tests maximum([3, 8, 5]) == 8 et maximum([4]) == 4 réussissent-ils ?
  2. Propose un test qui échoue et explique pourquoi.
  3. Quelle valeur renvoie cette fonction pour [-5, -2, -9] ?
  4. Corrige la fonction.
Indice 1

Regarde la valeur de départ de m.

Indice 2

Pense à un tableau ne contenant que des nombres négatifs.

Voir la correction pas à pas
  1. Pour [3, 8, 5] : m passe de 0 à 3, puis à 8, et reste à 8. Le résultat est 8, le test réussit. Pour [4] : m passe de 0 à 4, le test réussit aussi.

  2. Un tableau de nombres négatifs fait échouer la fonction : assert maximum([-5, -2, -9]) == -2. Aucun élément n'est supérieur à 0, donc m reste à 0.

  3. Pour [-5, -2, -9], la fonction renvoie 0. C'est faux : 0 n'est pas un élément du tableau, la postcondition est violée, alors que la valeur attendue est -2.

  4. Correction : initialiser m = t[0] (premier élément du tableau, qui existe car la précondition l'exige), puis garder la boucle inchangée.

Les deux tests ordinaires ne détectent rien : le défaut n'apparaît qu'avec un cas particulier (nombres négatifs). C'est pourquoi un jeu de test doit être varié.

Exercice 3 Écrire les tests avant le code

★★★Environ 20 min

On veut une fonction est_croissant(t) qui reçoit un tableau de nombres (éventuellement vide) et renvoie True si les éléments sont rangés dans l'ordre croissant au sens large (chaque élément est inférieur ou égal au suivant), False sinon.

  1. Écris la spécification.
  2. Écris un jeu de test d'au moins six assert, avant le code, qui couvre cas ordinaires, limites et particuliers.
  3. Écris la fonction.
  4. Vérifie mentalement que chaque test passe.
Indice 1

Que doit renvoyer la fonction pour un tableau vide ou d'un seul élément ? La spécification le dit : aucun élément n'est plus grand que son suivant.

Indice 2

Pense aux éléments égaux : « au sens large » les autorise.

Voir la correction pas à pas
  1. Spécification : est_croissant(t) ; paramètre t, liste de nombres, éventuellement vide ; postcondition : renvoie True si pour tout indice i valide, t[i] <= t[i+1], et False sinon.

  2. Jeu de test : assert est_croissant([1, 3, 8]) == True ; assert est_croissant([1, 3, 2]) == False ; assert est_croissant([]) == True ; assert est_croissant([5]) == True ; assert est_croissant([4, 4, 4]) == True ; assert est_croissant([9, 1]) == False ; assert est_croissant([1, 2, 2, 1]) == False.

  3. Code : def est_croissant(t): puis for i in range(len(t) - 1): avec if t[i] > t[i+1]: return False, et enfin return True après la boucle.

  4. Vérification : pour [] et [5], range(len(t) - 1) est vide (range(-1) ou range(0)), la boucle ne s'exécute pas et la fonction renvoie True. Pour [4, 4, 4], aucune comparaison 4 > 4 n'est vraie, donc True. Pour [1, 2, 2, 1], la comparaison 2 > 1 est vraie à la fin, donc False.

Les tests sur le tableau vide, le tableau à un élément et les éléments égaux sont les cas qu'on oublie le plus. Les écrire avant le code oblige à trancher ce que la spécification veut dire.

Quiz

8 questions · correction immédiate · ton score reste sur ton appareil.

  1. Que décrit une spécification d'une fonction ?

  2. Un test unitaire vérifie…

  3. Tous les assert d'un jeu de test réussissent. On peut en conclure que la fonction est forcément correcte pour toutes les entrées.

  4. D'où doit venir la sortie attendue d'un test ?

  5. Que se passe-t-il quand la condition d'un assert est fausse ?

  6. Parmi ces tests pour une fonction occurrences(t, x), lequel est un cas limite ?

  7. Une fonction affiche le résultat avec print mais n'a pas de return. On teste assert f(3) == 9. Que se passe-t-il, si la fonction affiche bien 9 ?

  8. Pourquoi recommande-t-on d'écrire les tests avant le code ?

Les questions qu'on se pose

Quelle différence entre une spécification et un test ?

La spécification dit ce que doit faire la fonction, en général pour toutes les entrées autorisées. Un test vérifie le comportement sur une entrée précise. La spécification donne donc la règle générale, et chaque test en contrôle un cas particulier 5.

Combien de tests faut-il écrire ?

Il n'y a pas de nombre magique. Il faut assez de tests pour couvrir les familles de cas : cas ordinaires, cas limites et cas particuliers. Une bonne indication consiste à vérifier que chaque instruction du code est exécutée par au moins un test, ce qui correspond à l'idée de couverture de code 5.

Le test d'une fonction peut-il échouer alors que mon code est juste ?

Oui. Le test peut contenir une erreur sur la sortie attendue, ou la spécification peut être ambiguë (par exemple, le tableau vide est-il autorisé ?). Quand un test échoue, relis la spécification et recalcule à la main la sortie attendue avant de modifier le code.

Qu'est-ce qui peut tomber à l'épreuve pratique à ce sujet ?

Le premier exercice demande de programmer un algorithme du programme à partir d'une spécification fournie 2. Le sujet peut donner des exemples avec leurs résultats, ou proposer de compléter un programme à trous 2. Lis donc toujours la spécification en entier et utilise les exemples comme premiers tests.

Faut-il toujours utiliser assert pour tester ?

En NSI, assert est l'outil le plus simple. Dans les grands projets, on utilise des cadriciels de tests unitaires qui facilitent l'écriture des tests 5. Le principe reste le même : confronter le résultat obtenu au résultat attendu.

Sources

  1. Programmes et ressources en numérique et sciences informatiques — voie générale, Éduscol. Consulté le 6 novembre 2026.
  2. Ressource officielle Éduscol — Spécification et tests, Éduscol. Consulté le 6 novembre 2026.
  3. Banque des épreuves pratiques de spécialité NSI, Éduscol. Consulté le 6 novembre 2026.
  4. Spécification (norme technique), Wikipédia. Consulté le 6 novembre 2026.
  5. Test unitaire, Wikipédia. Consulté le 6 novembre 2026.

Les numéros dans le texte renvoient à ces sources. Une source qui ne dit pas ce qu'on lui fait dire ? Signale-le.

Signaler une erreur sur cette page

Je suis en…

Primaire

  • CP Bientôt
  • CE1 Bientôt
  • CE2 Bientôt
  • CM1 Bientôt
  • CM2 Bientôt

Collège

Lycée

Supérieur

  • BTS Bientôt
  • BUT Bientôt
  • Licence Bientôt
  • Prépas Bientôt

Sur cette page