nose doctest plugin sucks ¶
En ce moment je bosse sur une application en Pylons. J'adore ce petit framework, mais y a un truc que je pouvais pas encadrer, c'est de faire des tests avec des TestCase. Je préfère de loin les doctests.
Me voilà donc partit à la recherche de docs pour pouvoir écrire mes tests comme j'aime les écrire. Pylons utilise nose comme framework de test. Je découvre alors avec joie que nose fournit un plugin pour parcourir les doctests. Chouet !
Le problème, c'est que ce plugin est carrément rudimentaire. En gros, il choppe vos doctest et les initialise ultra basiquement. Comprendre: impossible de passer des options telles que optionflag, setUp ou tearDown. En bref, ça pu. Comment je fais pour initialiser mon framework Pylons pour mes tests moi ? Hein ?
J'ai finalement trouvé une solution en surclassant la classe doctest.DocFileCase afin de faire ce que je veux. Voici le code en question. Il suffit de le placer dans le fichier tests/functional/test_docs.py de votre application Pylons:
# -*- coding: utf-8 -*-
import os
import doctest
import mypylonsapp
from mypylonsapp.tests import *
optionflags = (doctest.ELLIPSIS |
doctest.NORMALIZE_WHITESPACE |
doctest.REPORT_ONLY_FIRST_FAILURE)
dirname = os.path.join(os.path.dirname(mypylonsapp.__file__), 'docs')
def build_testcase(filename):
name = os.path.splitext(filename)[0]
path = os.path.join(dirname, filename)
class Dummy(doctest.DocFileCase, TestController):
def __init__(self, *args, **kwargs):
# init pylons stuff
TestController.__init__(self, *args, **kwargs)
# get tests from file
parser = doctest.DocTestParser()
doc = open(self.path).read()
test = parser.get_doctest(doc, globals(), name, self.path, 0)
# init doc test case
doctest.DocFileCase.__init__(self, test, optionflags=optionflags)
def setUp(self):
"""init pylons stuff and make app available in doctest
"""
TestController.setUp(self)
test = self._dt_test
test.globs['app'] = self.app
def tearDown(self):
"""cleaning
"""
TestController.tearDown(self)
test = self._dt_test
test.globs.clear()
# generate a new class for the file
return ("Test%s" % name.title(),
type('Test%sClass' % name.title(), (Dummy,), dict(path=path)))
for filename in os.listdir(dirname):
if filename == '.svn':
continue
name, klass = build_testcase(filename)
exec "%s = klass" % name
# clean namespace to avoid test duplication
del build_testcase, filename, name, klass
Vous admirerez la ruse qui est de générer une nouvelle classe pour chaque fichier trouvé dans le répertoire contenant les doctests.
On peut ensuite créer un fichier texte dans docs/ et y écrire des tests du genre:
>>> response = app.get(url_for(controller='main', action="index"))
>>> print response
Response: 200
...
Ce qui est tout de même vachement plus convi qu'un test classique.





