2009-10-23 2 views
10

Если я запускаю следующую команду:Настройка Django для поиска всех доктрин во всех модулях?

>python manage.py test 

Django смотрит на tests.py в моем приложении, и запускает любые doctests или юнит-тестов в этом файле. Он также рассматривает словарь __ test __ для выполнения дополнительных тестов. Так что я могу связать doctests из других модулей, как так:

#tests.py 
from myapp.module1 import _function1, _function2 

__test__ = { 
    "_function1": _function1, 
    "_function2": _function2 
} 

Если я хочу, чтобы включить больше doctests, есть более простой способ, чем перечислять их все в этом словаре? В идеале я просто хочу, чтобы Django находил все доктрины во всех модулях приложения myapp.

Есть ли какой-то рефлекторный хак, который доставит меня туда, где я хочу быть?

ответ

1

Благодаря Алексу и Павлу. Это то, что я придумал:

# 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) 
+0

Как вы обрабатываете модули глубже 1-го уровня ('myapp.views.stats')? –

+0

os.walk() следует добавить для этого. –

+0

Рад помочь. Похож на полезный фрагмент. –

1

Here're ключевые элементы решения:

tests.py:

def find_modules(package): 
    """Return list of imported modules from given package""" 
    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 ('__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)]) 

Чтобы добавить использование рекурсии os.walk() для обхода модуля дерева и найти питона пакетов.

+0

Мне нравится метод Павла для поиска самих модулей. Вы предоставили код для получения документов. Позаботьтесь о закрытии цикла и постройте словарь __ test __? –

2

Я решил это для себя некоторое время назад:

 
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 

Это позволило мне поставить каждого модуля тестов в их собственном файле test.py, поэтому они не перепутать с остальной частью моего код приложения. Было бы легко изменить это, чтобы просто искать тесты doc в каждом из ваших модулей и запускать их, если они их обнаружат.

+0

Это отлично подходит для модульных тестов. Благодаря! Если вы действительно можете составить код с рабочим примером для тестов doc, я буду рад наградить очки бонусов. –

2

Использование django-nose поскольку нос автоматически найдет все тесты recursivelly.

+0

Казалось, что это хорошая идея, но (а) она не автоматическая, чтобы найти доктрины, и (б) мои попытки сказать ей, чтобы найти доктрины, казалось, не имели никакого эффекта – Auspex

1

Я не досконально тестирую Djano, но, как я понимаю, он использует автоматическое обнаружение unittest, как и python -m unittest discover и Nose.

Если это так, просто поместите следующий файл там, где обнаружение обнаружит его (обычно это просто вопрос об имени test_doctest.py или тому подобное).

Изменить your_package к упаковке для проверки. Все модули (включая подпакеты) будут рассмотрены.

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 
Смежные вопросы