Écrire un programme, c'est bien mais c'est encore mieux s'il est parfaitement juste et que l'on peut être sûr qu'il fonctionnera en toute circonstance. C'est ce que l'on appelle prouver un programme mais cela nécessite la maîtrise d'un langage mathématique fondé sur la logique classique, l'arithmétique et la théorie des ensembles que nous n'aborderons pas dans ce cours.
Une première étape vers ce processus de preuve d'un programme est d'apprendre à définir précisément ce dont le programme a besoin et ce qu'il doit répondre. C'est ce que l'on appelle prototyper une fonction ou bien écrire ses spécifications.
Voici une fonction écrite en Python, convertissant un nombre décimal en binaire sur un nombre de bits donné en paramètre.
Pour utiliser cette fonction, il est nécessaire de savoir ce qu'elle fait et sous quelles
conditions. Nous pouvons ajouter dans le corps de la fonction, des commentaires sur le bon
usage de cette fonction. C'est ce que l'on appelle documenter une fonction. C'est cette
documentation qui est renvoyée lorsque la fonction est donnée en argument de la fonction
native
Voyons comment l'utiliser pour vérifier les préconditions pour la fonction précédente :
Le mécanisme d'assertion permet de résoudre des erreurs. En effet, en arrêtant l'exécution avant que l'erreur se produise et en la documentant, il permet de se rendre immédiatement compte d'un problème dans le programme et de le corriger.
De même on peut insérer en fin de fonction, juste avant le
Ce mode de programmation qui utilise les assertions pour vérifier les préconditions et les postconditions, est appelé programmation défensive. C'est utilisé pour du code dont on a le contrôle, en cours de développement par exemple.
Lorsqu'on écrit un module, on peut programmer défensivement les fonctions qui sont destinées à être
seulement appelées au sein de ce dernier. Pour des fonctions destinées à être utilisées par
d'autres personnes, en dehors du module, on va plutôt faire une gestion active des erreurs,
notamment avec des structures conditionnelle
Il est alors préférable, lorsqu'on définit et spécifie une nouvelle fonction, de minimiser le nombre de préconditions si on veut la rendre robuste, c'est-à-dire résistante à de mauvaises utilisations sans qu'elle ne génère d'erreur. Elle doit alors être capable de gérer les différents mésusages. C'est particulièrement nécessaire lorsque cette fonction est vouée à être utilisée directement par l'utilisateur. Cependant, pour un usage interne à un programme, c'est moins critique car vous maîtrisez tous les appels et le code de la fonction n'en sera que plus simple, débarassée des assertions.
Il faut comprendre que le mécanisme d'assertion est une aide au développeur, et ne doit
en aucun cas faire partie du code final d'un programme. En supprimant toutes les instructions
Le développement piloté par des tests (TDD) est une méthode d'écriture de programme qui met en avant le fait d'écrire d'abord des jeux de tests pour chaque fonction du programme (dans ses spécifications) puis d'écrire le code qui permettra au programme de passer ces tests avec succès.
Cette manière de procéder permet de penser plus profondément les spécifications d'un programme, en amont de l'écriture du code. Cela :
La qualité et le nombre de tests sont importants pour atteindre la qualité souhaitée du code mais un jeu de tests ne pourra jamais être exhaustif et prouver la correction d'un programme (garantir qu'il n'y aura pas d'erreur).
Un jeu de tests doit s'attacher à vérifier tous les cas particuliers et les cas limites d'usage pour être réellement utile. Il est inutile de tester toutes les possibilités.
Voici un jeu de tests pour la fonction
L'inconvénient d'insérer des assertions directement après une fonction pour la tester est que celles-ci seront exécutées tout le temps, à chaque exécution du script, ce qui peut entraîner une perte de temps. Lorsque le script est importé en tant que module par un autre script, on préfère éviter ces tests. Cela est possible en utilisant une structure conditionnelle :
Cette structure permet de vérifier que l'attribut
Le module doctest permet d'inclure le jeu de tests à l'intérieur du corps de la fonction directement dans sa documentation. Voici le code correspondant :
verbose=True
permet d'afficher le
test même s'il est réussi. Si on veut afficher seulement les tests qui échouent
on mettra verbose=False
(valeur par défaut).
Remarque : Le module doctests est assez utile mais atteint vite ses limites lorsque les structures de données à vérifier sont plus complexes, comme avec les dictionnaires où l'ordre des éléments n'est pas garanti.
Bibliographie :