代码之家  ›  专栏  ›  技术社区  ›  Chase Seibert

配置django以查找所有模块中的所有doctest?

  •  10
  • Chase Seibert  · 技术社区  · 15 年前

    如果我运行以下命令:

    >python manage.py test
    

    Django在我的应用程序中查看tests.py,并在该文件中运行任何doctest或单元测试。它还查看测试字典,以便运行额外的测试。因此,我可以将doctest与其他模块链接起来,比如:

    #tests.py
    from myapp.module1 import _function1, _function2
    
    __test__ = {
        "_function1": _function1,
        "_function2": _function2
    }
    

    如果我想包括更多的教义,有没有比在这本词典中列举它们更容易的方法?理想情况下,我只想让Django在myapp应用程序的所有模块中找到所有的doctest。

    有没有什么反射式黑客能帮我找到我想去的地方?

    5 回复  |  直到 10 年前
        1
  •  2
  •   Paul McMillan    15 年前

    我刚才自己解决了这个问题:

    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文件中,这样它们就不会与我的应用程序代码的其余部分混淆。只需在每个模块中查找文档测试,并在找到它们时运行它们,就可以很容易地修改它。

        2
  •  2
  •   Bite code    12 年前

    使用 django-nose 因为nose会自动递归地查找所有测试。

        3
  •  1
  •   Alexander Lebedev    15 年前

    以下是解决方案的关键要素:

    测试:

    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包。

        4
  •  1
  •   Chase Seibert    15 年前

    感谢亚历克斯和保罗。这就是我想到的:

    # 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)
    
        5
  •  1
  •   Søren Løvborg    10 年前

    我没有跟上德加诺的测试速度,但据我所知,它使用自动 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