Exerciciels d’algorithmique avec les tests unitaires

samedi 27 octobre 2012
par  Alain BUSSER

Les tests unitaires servent à déboguer des programmes ; on peut aussi en faire un usage pédagogique. Voici comment, en quelques lignes de Python

En fait, tous les langages objet ont un utilitaire de test :

  • JUnit en Java
  • PHPUnit pour Php
  • JSUnit pour JavaScript
  • PyUnit pour Python
  • SUnit pour Smalltalk

Etc.

Le principe est toujours le même :

  1. Le prof écrit un certain nombre d’affirmations (« assert ») qui devront être vraies si l’algorithme est bien rédigé (une sorte de cahier des charges) ;
  2. L’élève rédige son algorithme en Python (par exemple)
  3. L’élève lance le test créé par le prof, et regarde si le test réussit ; si le test échoue, l’élève sait où et en déduit une remédiation.

L’élève est donc très largement autonome. Le test lui-même, selon la façon dont il est rédigé, peut mettre l’élève sur la voie pour la rédaction de l’algorithme ... ou pas !

En amont

L’énoncé est le suivant :

Rédiger une fonction f en Python, qui admet en entrée un entier n, et retourne en sortie la somme des entiers inférieurs ou égaux à n.

Le prof prépare donc son sujet d’interro sous la forme d’une fonction interro() qui ne fait que vérifier certaines valeurs de f :

variantes

Il est possible de mettre cette fonction dans un fichier du source de Python sans le dire aux élèves ; par exemple le fichier prof.py (qui n’existe pas !). L’élève devra alors écrire

from prof import *

en préambule de son exercice. Ce qui lui simplifie la tâche s’il doit redémarrer Python.

Il est possible aussi de rajouter, outre les assertions, un calcul automatique de la note obtenue. Dans ce cas, le mieux est de définir la fonction interro avec pour argument le nom de l’élève. Quelque chose comme

def interro(eleve):
    assert(f(1)==1)
    note(eleve)=5
    assert(f(2)==3)
    note(eleve)=10
    assert(f(3)==6)
    note(eleve)=15
    assert(f(4)==10)
    note(eleve)=20

L’élève Vincent devra alors, après avoir entré sa fonction, tester avec

interro(Vincent)

Contre-exemple

Si un élève tente une interpolation affine, et se rend compte que la fonction 2x-1 coïncide avec la somme des premiers entiers pour n=1 ou n=2 :

  • en effet 2×1-1=2-1=1 ;
  • et 2×2-1=4-1=3...

Alors il va écrire une fonction comme

def f(x):
    return 2*x-1

Qui aura l’air correcte pour x=1 et x=2 mais le test échoue :

Lumineux non ? Avec un minimum de connaissance de la langue anglaise, on voit que l’algorithme n’est pas bon parce que l’image de 3 n’est pas égale à 6. En effet 2×3-1=6-1=5 et pas 6...

Exemples

En fait, c’est tellement facile de faire des interros avec cet outil de test, qu’on a vite peur du faux positif (un test qui échoue alors que l’algorithme est correct). Mais plusieurs algorithmes différents donnent un test réussi (qu’on aperçoit au fait qu’il n’y a pas de message d’erreur). Par exemple avec une sommation par boucle :

Ou la version raccourcie avec itérateur (peut-on encore parler d’algorithme ici ?) :

Et même avec un polynôme d’interpolation, avec des valeurs approchées réelles (donc a priori pas égales aux entiers voulus) le test réussit :

Triche

Si un élève a accès au source de l’interro(), il peut écrire une fonction ad hoc qui ne passe victorieusement que la batterie de tests préétablie. Mais l’écriture de cet algorithme (une fonction définie par intervalles par exemple) devient vite fastidieuse et le tricheur finira même par mériter sa note !

Par contre, si le test est caché aux élèves, on peut le rendre aléatoire. Avec des choses comme

for n in range(6):
    t=randrange(100)
    assert(f(t)==t*(t+1)/2)

Barème

On peut faire quelque chose comme ceci : 6 assertions dans le test (précautionneusement choisies) comptant 4 points chacune, ce qui fait un total de 24 points ; auxquels on enlève 2 points par ligne de programme, le total étant tronqué à 20 pour ceux qui auraient réussi à écrire un programme d’une seule ligne...

Les assertions sur la position (coordonnées et orientation) de la tortue permettent aussi de faire des exerciciels d’algorithmique dans un cadre graphique avec le module turtle()...

Il est également possible de transformer les interros en expériences aléatoires, et de mutualiser les résultats des élèves, pour amener à la statistique inférentielle (compter par exemple le pourcentage de tests réussis si ces tests sont basés sur un intervalle de confiance, et ainsi évaluer expérimentalement le degré de confiance de cet intervalle).

Conclusion : Ça sert, cet assert !


Commentaires