2009-10-23 12 views
10

Jeżeli uruchomić następujące polecenie:Skonfiguruj Django, aby znaleźć wszystkie doctesty we wszystkich modułach?

>python manage.py test 

Django wygląda na tests.py w mojej aplikacji i uruchamia żadnych doctests lub testów jednostkowych w tym pliku. Sprawdza także słownik __ test __, aby uruchomić dodatkowe testy. Więc mogę połączyć doctests z innymi modułami tak:

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

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

Jeśli chcę zawierać więcej doctests, czy istnieje prostszy sposób niż wymieniając je wszystkie w tym słowniku? Idealnie, po prostu chcę, aby Django znalazł wszystkie doctesty we wszystkich modułach aplikacji myapp.

Czy jest jakiś rodzaj hackowania refleksji, który doprowadziłby mnie tam, gdzie chcę być?

Odpowiedz

1

Podziękowania dla Alexa i Paula. To właśnie wymyśliłem:

# 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

Jak radzisz sobie z modułami głębszymi niż 1. poziom ('myapp.views.stats')? –

+0

os.walk() należy do tego dodać. –

+0

Cieszę się, że mogę pomóc. Wygląda jak przydatny fragment. –

1

Here're kluczowe elementy rozwiązania:

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)]) 

Aby dodać stosowanie rekurencji os.walk() przemierzać modułu drzewo i znaleźć pakiety Pythona.

+0

Podoba mi się metoda znalezienia modułów przez Paula. Podałeś kod, aby uzyskać testy doc. Chcesz zamknąć pętlę i skonstruować słownik __ test __? –

2

Rozwiązałem to dla mnie jakiś czas temu:

 
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 

To pozwoliło mi umieścić testy na moduł we własnym pliku test.py, więc oni nie mieszają się z resztą mojego kod aplikacji. Łatwo byłoby to zmodyfikować, aby wyszukać testy doc w każdym z modułów i uruchomić je, jeśli je znajdzie.

+0

Jest to idealne rozwiązanie do testów jednostkowych. Dzięki! Jeśli faktycznie możesz kodować za pomocą działającego przykładu testów doktora, z przyjemnością nagrodzę punkty za bounty. –

2

Zastosowanie django-nose od nosa automatycznie znaleźć wszystkie testy recursivelly.

+0

Wydaje się, że jest to dobry pomysł, ale (a) nie jest automatyczne wyszukiwanie dowodów, i (b) moje próby powiedzenia mu znalezienia doctów wydawały się nie mieć wpływu – Auspex

1

Nie testuję Djano, ale jak rozumiem, wykorzystuje automatyczne wykrywanie unittest, podobnie jak python -m unittest discover i nos.

Jeśli tak, po prostu umieść następujący plik w miejscu, w którym odnajdzie go (zwykle jest to tylko kwestia nazwania go test_doctest.py lub podobnym).

Zmień your_package na pakiet do przetestowania. Wszystkie moduły (w tym podpakiety) zostaną poddane testom.

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 
Powiązane problemy