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 :
- Avant d'écrire le code, tu décris ce que la fonction doit faire (la spécification).
- Tu écris le code qui réalise cette description.
- 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.
- SpécifierEntrées, précondition, postcondition
- TesterÉcrire les assert avant le code
- CoderÉcrire la fonction demandée
- VérifierRelancer les tests et corriger
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
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 et (bornes incluses).
- Écris la docstring de spécification (précondition et postcondition).
- Propose trois
assertdont un cas limite. - 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
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. »
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) etassert somme_pairs(4) == 6(0 + 2 + 4).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
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
- Les tests
maximum([3, 8, 5]) == 8etmaximum([4]) == 4réussissent-ils ? - Propose un test qui échoue et explique pourquoi.
- Quelle valeur renvoie cette fonction pour
[-5, -2, -9]? - 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
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.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.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.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
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.
- Écris la spécification.
- Écris un jeu de test d'au moins six
assert, avant le code, qui couvre cas ordinaires, limites et particuliers. - Écris la fonction.
- 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
Spécification :
est_croissant(t); paramètret, liste de nombres, éventuellement vide ; postcondition : renvoieTruesi pour tout indiceivalide,t[i] <= t[i+1], etFalsesinon.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.Code :
def est_croissant(t):puisfor i in range(len(t) - 1):avecif t[i] > t[i+1]: return False, et enfinreturn Trueaprès la boucle.Vérification : pour
[]et[5],range(len(t) - 1)est vide (range(-1)ourange(0)), la boucle ne s'exécute pas et la fonction renvoie True. Pour[4, 4, 4], aucune comparaison4 > 4n'est vraie, donc True. Pour[1, 2, 2, 1], la comparaison2 > 1est 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.
-
Une spécification est l'ensemble des exigences à satisfaire 4 : pour une fonction, entrées, sortie attendue et conditions. Décrire les instructions utilisées, c'est décrire le « comment », donc le code, et non la spécification, qui répond à « quoi ».
-
Le test unitaire porte sur une unité ou un module 5. Tester le programme entier d'un seul bloc rend difficile la localisation d'un défaut : c'est justement ce que le test unitaire évite.
-
Faux. Un jeu de test fini ne couvre pas toutes les entrées possibles : il montre que la fonction est correcte sur les cas testés. La version fautive de
maximumqui part de 0 réussit les tests ordinaires et échoue sur des nombres négatifs. -
Le test confronte la réalisation à la spécification 5 : la valeur attendue vient donc de la spécification. Si on la copie depuis le résultat du code, le test réussit toujours, même quand le code est faux.
-
Un
assertfaux lève une erreurAssertionErrorqui interrompt le programme. Quand la condition est vraie, il ne fait rien, mais ne s'affiche pas non plus : c'est pour cela qu'on ajoute unprintfinal dans une fonction de test pour signaler le succès. -
Le tableau vide se situe à la frontière de ce que la spécification autorise, c'est un cas limite. Les autres sont des cas ordinaires ; répéter dix fois le même test n'apporte aucune information nouvelle.
-
Sans
return, une fonction Python renvoieNone. La comparaisonNone == 9est fausse, donc leassertéchoue. L'affichage à l'écran n'est pas une valeur de retour. -
Les tests doivent refléter la spécification, et les écrire avant le code limite le risque d'être influencé par celui-ci 5. Python n'impose aucun ordre, et la spécification reste nécessaire pour savoir quoi tester.
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
- Programmes et ressources en numérique et sciences informatiques — voie générale, Éduscol. Consulté le 6 novembre 2026.
- Ressource officielle Éduscol — Spécification et tests, Éduscol. Consulté le 6 novembre 2026.
- Banque des épreuves pratiques de spécialité NSI, Éduscol. Consulté le 6 novembre 2026.
- Spécification (norme technique), Wikipédia. Consulté le 6 novembre 2026.
- 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.