Le code des fonctions devrait être testé, et ce d’autant plus qu’il pourrait être utilisé par d’autres développeurs.
Les tests sont des séquences d’instructions qui vérifient que le comportement d’une fonction est conforme à sa spécification.
Important : un test ne prouve jamais l’absence d’erreur : son rôle est de repérer les erreurs afin qu’elles puissent être corrigées.
L’écriture de tests permet aussi de démontrer la qualité de son travail. Ainsi, le “ça marchait hier chez moi” devient “voici les tests programmés et le rapport associé ; je vais développer de nouveaux tests pour prendre en compte ce cas qui n’avait pas encore été pris en considération et corriger l’application.”
Les “doctests” sont des tests intégrés directement dans la documentation d’une fonction. Ils servent à la fois d’exemples d’utilisation pour les développeurs (doc) et à tester les fonctions (ce sont des tests dits unitaires). Une fonction comporte généralement plusieurs “doctests" ; un “doctest” s’écrit en deux lignes :
>>> nomFonction(paramètres)
valeurRenvoyéeAttendue
Les tests sont automatiquement exécutés grâce aux instructions import doctest; doctest.testmod() ;
Cette instruction ne devrait pas être exécutée en production ; elle peut être
mise en commentaire (par exemple).
Exemple :
def carre(nombre: int) -> int:
""" Calcule le carre d'un nombre.
@param nombre le nombre dont on veut le carré
@return nombre²
>>> carre(2) #le résultat attendu du premier test 2² est 4
4
>>> carre(3) #deuxième test
9
"""
return nombre * nombre #ou: return nombre ** 2
if __name__ == "__main__":
import doctest; doctest.testmod() #ou sur deux lignes
Attention, en cas d’échec d’un test, plusieurs causes sont possibles :
- le code de la fonction est erroné ;
- la valeur attendue au résultat du test est erronée.
Une classe d’équivalence est un ensemble de valeurs pour lesquelles la fonction devrait avoir le même comportement ; au lieu de tester toutes les valeurs possibles, seule une valeur représentative de chaque classe est testée.
Les valeurs limites sont les frontières entre les classes d’équivalence ; elles doivent également être testées.
Exemple : l’obtention du BTS
requiert une moyenne de 10, ce qui fait deux classes d’équivalence moyenne ∈ ⟦0..10⟦
(9 par exemple) et moyenne ∈ ⟦10..20⟧ (11 par exemple), et 10 comme valeur limite.
from typing import List
from statistics import mean
def btsObtenu(notes: List[float]) -> bool:
""" Indique si le BTS a été obtenu selon les notes.
@param notes les notes de la personne candidate (toutes avec le même coefficient)
@return True si le BTS a été obtenu (moyenne ≥ 10), False sinon
précondition: il y a au moins une note
Classes d'équivalences:
>>> btsObtenu([8, 10, 9]) #moyenne de 9
False
>>> btsObtenu([8, 14, 11]) #moyenne de 11
True
Valeurs limites:
>>> btsObtenu([9, 10, 11]) #moyenne de 10
True
"""
assert len(notes) != 0, "il faut au moins une note"
if mean(notes) < 10: #mean calcule la moyenne
resultat = False
else:
resultat = True
return resultat
if __name__ == "__main__":
import doctest; doctest.testmod()
Certains développeurs écrivent même les tests avant le corps de la fonction ; c’est le TDD ; cette approche permet de limiter la dette technique liée au code non testé et permet souvent un gain de temps : le développement est terminé lorsque les tests passent.