Настроить Django для поиска всех тестов во всех модулях?

Если я запустил следующую команду:

>python manage.py test

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

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

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

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

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


person Chase Seibert    schedule 23.10.2009    source источник


Ответы (5)


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

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, чтобы они не смешивались с остальным кодом моего приложения. Было бы легко изменить это, чтобы просто искать тесты документации в каждом из ваших модулей и запускать их, если он их нашел.

person Paul McMillan    schedule 29.10.2009
comment
Это отлично подходит для модульных тестов. Спасибо! Если вы действительно можете создать рабочий пример для тестирования документации, я буду рад наградить вас бонусными баллами. - person Chase Seibert; 31.10.2009

Используйте django-нос, так как Нос автоматически находит все тесты рекурсивно.

person e-satis    schedule 09.04.2012
comment
Казалось бы, хорошая идея, но (а) поиск доктестов происходит не автоматически, и (б) мои попытки сказать ему, что нужно найти доктесты, не дали никакого эффекта - person Auspex; 15.04.2014

Вот ключевые элементы решения:

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() для обхода дерева модулей и поиска пакетов Python.

person Alexander Lebedev    schedule 29.10.2009
comment
Мне нравится метод Пола поиска самих модулей. Вы предоставили код для тестирования документации. Не хотите ли закрыть цикл и построить словарь __ test __? - person Chase Seibert; 31.10.2009

Спасибо Алексу и Полу. Вот что я придумал:

# 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)
person Chase Seibert    schedule 31.10.2009
comment
Как вы работаете с модулями глубже 1-го уровня (myapp.views.stats)? - person Alexander Lebedev; 31.10.2009
comment
Для этого следует добавить os.walk (). - person Chase Seibert; 31.10.2009
comment
Рад помочь. Похоже на полезный сниппет. - person Paul McMillan; 01.11.2009

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

Если это так, просто поместите следующий файл в то место, где его найдет обнаружение (обычно это просто вопрос имени 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
person Søren Løvborg    schedule 10.10.2014