Configurer Django pour trouver tous les doctests dans tous les modules?
-
06-07-2019 - |
Question
Si j'exécute la commande suivante:
>python manage.py test
Django examine tests.py dans mon application et exécute tous les doctests ou les tests unitaires de ce fichier. Il examine également le dictionnaire __ test __ pour des tests supplémentaires à exécuter. Je peux donc relier des docttes d’autres modules comme ceci:
#tests.py
from myapp.module1 import _function1, _function2
__test__ = {
"_function1": _function1,
"_function2": _function2
}
Si je veux inclure plus de doctests, existe-t-il un moyen plus simple que de les énumérer tous dans ce dictionnaire? Idéalement, je veux juste que Django trouve tous les doctests dans tous les modules de l’application myapp.
Y a-t-il une sorte de bidouille de réflexion qui me conduirait là où je veux être?
La solution 4
Merci à Alex et Paul. C’est ce que j’ai trouvé:
# tests.py
import sys, settings, re, os, doctest, unittest, imp
# import your base Django project
import myapp
# Django already runs these, don't include them again
ALREADY_RUN = ['tests.py', 'models.py']
def find_untested_modules(package):
""" Gets all modules not already included in Django's test suite """
files = [re.sub('\.py, '', f)
for f in os.listdir(os.path.dirname(package.__file__))
if f.endswith(".py")
and os.path.basename(f) not in ALREADY_RUN]
return [imp.load_module(file, *imp.find_module(file, package.__path__))
for file in files]
def modules_callables(module):
return [m for m in dir(module) if callable(getattr(module, m))]
def has_doctest(docstring):
return ">>>" in docstring
__test__ = {}
for module in find_untested_modules(myapp.module1):
for method in modules_callables(module):
docstring = str(getattr(module, method).__doc__)
if has_doctest(docstring):
print "Found doctest(s) " + module.__name__ + "." + method
# import the method itself, so doctest can find it
_temp = __import__(module.__name__, globals(), locals(), [method])
locals()[method] = getattr(_temp, method)
# Django looks in __test__ for doctests to run
__test__[method] = getattr(module, method)
Autres conseils
J'ai résolu ce problème il y a quelque temps:
apps = settings.INSTALLED_APPS for app in apps: try: a = app + '.test' __import__(a) m = sys.modules[a] except ImportError: #no test jobs for this module, continue to next one continue #run your test using the imported module m
Cela m'a permis de mettre des tests par module dans leur propre fichier test.py, afin qu'ils ne soient pas mélangés avec le reste de mon code d'application. Il serait facile de modifier cela en recherchant simplement des tests de doc dans chacun de vos modules et en les lançant s’ils les trouvaient.
Utilisez django-nose , car nose trouve automatiquement tous les tests de manière récursive.
Voici les éléments clés de la solution:
tests.py:
def find_modules(package):
"""Return list of imported modules from given package"""
files = [re.sub('\.py
Pour ajouter de la récursivité, utilisez os.walk ()
pour parcourir l’arborescence des modules et rechercher les packages Python.
, '', f) for f in os.listdir(os.path.dirname(package.__file__))
if f.endswith(".py") and os.path.basename(f) not in ('__init__.py', 'test.py')]
return [imp.load_module(file, *imp.find_module(file, package.__path__)) for file in files]
def suite(package=None):
"""Assemble test suite for Django default test loader"""
if not package: package = myapp.tests # Default argument required for Django test runner
return unittest.TestSuite([doctest.DocTestSuite(m) for m in find_modules(package)])
Pour ajouter de la récursivité, utilisez os.walk ()
pour parcourir l’arborescence des modules et rechercher les packages Python.
Je ne suis pas au courant des tests de Djano, mais si je comprends bien, il utilise automatiquement les découverte unittest , tout comme python -m unittest discover
et Nose.
Si tel est le cas, placez simplement le fichier suivant quelque part dans lequel la découverte le trouvera (il suffit généralement de le nommer test_doctest.py
ou similaire).
Remplacez votre_paquet
par le paquet à tester. Tous les modules (y compris les sous-paquetages) feront l’objet d’une modification.
import doctest
import pkgutil
import your_package as root_package
def load_tests(loader, tests, ignore):
modules = pkgutil.walk_packages(root_package.__path__, root_package.__name__ + '.')
for _, module_name, _ in modules:
try:
suite = doctest.DocTestSuite(module_name)
except ValueError:
# Presumably a "no docstrings" error. That's OK.
pass
else:
tests.addTests(suite)
return tests