【问题标题】:Configure Django to find all doctests in all modules?配置 Django 以查找所有模块中的所有文档测试?
【发布时间】:2009-10-23 19:19:44
【问题描述】:

如果我运行以下命令:

>python manage.py test

Django 查看我的应用程序中的tests.py,并在该文件中运行任何文档测试或单元测试。它还查看 __ test __ 字典以运行额外的测试。所以我可以像这样链接来自其他模块的文档测试:

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

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

如果我想包含更多的文档测试,有没有比在这本字典中枚举它们更简单的方法?理想情况下,我只想让 Django 在 myapp 应用程序的所有模块中找到所有 doctest。

是否有某种反射技巧可以让我到达我想去的地方?

【问题讨论】:

    标签: django doctest django-testing


    【解决方案1】:

    我不久前自己解决了这个问题:

    应用程序 = settings.INSTALLED_APPS 对于应用程序中的应用程序: 尝试: a = 应用程序 + '.test' __import__(a) m = sys.modules[a] 除了 ImportError: #no test jobs for this module, 继续下一个 继续 #使用导入的模块 m 运行测试

    这让我可以将每个模块的测试放在他们自己的 test.py 文件中,因此它们不会与我的应用程序代码的其余部分混淆。修改这个很容易,只需在每个模块中查找文档测试,如果找到就运行它们。

    【讨论】:

    • 这非常适合单元测试。谢谢!如果您实际上可以为文档测试编写一个工作示例,我将很乐意奖励赏金。
    【解决方案2】:

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

    【讨论】:

    • 似乎是个好主意,但是 (a) 查找 doctest 不是自动的,并且 (b) 我试图告诉它查找 doctest 似乎没有效果
    【解决方案3】:

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

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

    【讨论】:

    • 我喜欢 Paul 自己查找模块的方法。您已经提供了获取文档测试的代码。想关闭循环并构造__ test __ 字典吗?
    【解决方案4】:

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

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

    【讨论】:

    • 如何处理比第一级更深的模块 (myapp.views.stats)?
    • os.walk() 应该为此添加。
    • 很高兴能提供帮助。看起来像一个有用的 sn-p。
    【解决方案5】:

    我没有跟上 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
    

    【讨论】:

      猜你喜欢
      • 2018-09-04
      • 1970-01-01
      • 2015-12-15
      • 2017-08-21
      • 2016-11-09
      • 2018-09-16
      • 1970-01-01
      • 2019-01-12
      相关资源
      最近更新 更多