Version 2, last updated by abergeron at April 19, 2010 09:05 UTC
Système de tests
J'ai ajouté un script simple pour tester avec doctest les modules que l'on développe.
Exemple simple
Les exemples sont parlants alors en voici un:
def print_hello(times):
r"""
Prints hello `times` times.
Tests:
>>> print_hello(1)
hello
>>> print_hello(3)
hello
hello
hello
"""
for i in range(times):
print "hello"
Dans cet example la portion entre r""" et """ est appelée le docstring. Le script scanne tous les docstring dans le code pour les lignes qui commence par >>> (en excluant des espaces blancs au début). Toutes ces lignes sont exécutées et la sortie est comparée au résultat attendu écrit en dessous. Si c'est pareil, tout va bien sinon, une erreur est reportée.
Les lignes d'un même bloc de tests sont exécutées une après l'autre en conservant les variables définies et autres facteurs de l'environnement. Il est donc possible de faire des test qui comptent sur ceux qui précèdent.
r"""
>>> a = 5
>>> b = 3
>>> a + b
8
"""
Par contre, l'environnement est mis à zéro entre les blocs donc il ne faut pas compter sur l'effet d'un autre test dans ses tests.
L'environnement accessible dans les tests comprend toutes les fonctions et classes définies dans le fichier contenant les tests ainsi que tout ce qui est directement accessible à partir de ift6266 (rien au moment de l'écriture). Si vous avez besoin d'autres modules il faut les importer avant des les utiliser.
Traitement des exceptions
Si veut tester qu'une fonction lance une exception dans un certain contexte on le fait ainsi
def test_ex():
r"""
>>> test_ex()
Traceback (most recent call last):
...
Exception:
"""
raise Exception
L'ellipse (...) signifie que l'on n'est pas intéressé par le détail de la provenace de l'exception.
Sortie aléatoires
Si on a une fonction dont une portion de sa sortie est aléatoire on peut tester le reste de la sortie en masquant cette portion
import random
def print_rand(max):
r"""
>>> print_rand(6)
Nombre choisi: ...
"""
print "Nombre choisi:", random.randint(0, max)
Encore une fois la présence d'une ellipse (...) indique que l'on n'est pas intéressé pas les détails. Ce qui est autour doit correspondre par contre.
Résultats
Lorsque qu'aucune erreur n'est détectée le script affiche la liste des modules testés
[bergearn@maggie46 ift6266]$ python test.py
Testing: ift6266.datasets.dataset
Testing: ift6266.datasets.dsetiter
Testing: ift6266.datasets.ftfile
Testing: ift6266.datasets.nist
[bergearn@maggie46 ift6266]$
Si un des tests signale une erreur la sortie devient
[bergearn@maggie46 ift6266]$ python test.py
Testing: ift6266.datasets.dataset
Testing: ift6266.datasets.dsetiter
**********************************************************************
File "/u/bergearn/local/lib/python2.5/site-packages/ift6266/datasets/dsetiter.py", line 27, in ift6266.datasets.dsetiter.DataIterator.__init__
Failed example:
d.batchsize
Expected:
11
Got:
10
**********************************************************************
1 items had failures:
1 of 24 in ift6266.datasets.dsetiter.DataIterator.__init__
***Test Failed*** 1 failures.
Testing: ift6266.datasets.ftfile
Testing: ift6266.datasets.nist
[bergearn@maggie46 ift6266]$
Dans cet exemple il y avait un test exécutant
>>> d.batchsize
qui s'attendait à recevoir 11 mais a reçu 10. On a en haut le fichier contenant le test qui n'a pas passé et la ligne du test. (Cet exemple est tiré de mon code pour les datasets)
Autres détails (*important*)
Le script traverse récursivment tous les sous-modules de ift6266 (tous les répertoires contenant un fichier __init__.py) à l'exception de scripts (qui est inconditonnelement ignoré parce que la plupart des fichiers contenus s'exécutent lorsqu'il sont importés et même certains lancent des jobs sur le cluster)
Faites attention à ce que les fichiers python soit protégés contre l'importation pour que le script de test ne lance pas de travail. Pour se protéger, on met le code que l'on veut exécuter lorsqu'on tape python <fichier>.py dans une section
if __name__ == '__main__':
<code à exécuter ici>