【问题标题】:Python: What's the right way to make modules visible to a TestRunner?Python:使模块对 TestRunner 可见的正确方法是什么?
【发布时间】:2012-05-14 08:38:24
【问题描述】:

在观看了一些关于 django 测试的演示后,我想编写自己的 TestRunner 以跳过 django 测试,并为我的测试创建更好的包结构。

问题是我们改变了项目结构,测试运行器找不到正确的路径来进行测试发现。这是我的项目的样子:

project/
  -src/
    - project_name/
      - apps/
      - test/ # Not a good name, i know, will change it
         - some_app/

           - test_models.py
    - manage.py
    - development.db

现在,为了测试test_models.py,我想这样做:

$ cd project/src/
$ python manage.py test some_app.test_models

问题是测试运行器找不到那个包 (some_app) 和模块 (test_models.py)。如果我在测试运行程序中硬编码名称,它会改变,但我不喜欢这样做。以下是我为使其发挥作用所做的工作。

test_labels = ["%s.%s" % ("project_name.test", l)
                           for l in test_labels
                           if not l.startswith("project_name.test")]

所以,如果你这样做了

$ python manage.py test some_app.test_models

它将被重写为:

$ python manage.py test project_name.test.some_app.test_models

而且效果很好。

我尝试过sys.path.append("(...)/project_name/test),但也没有用。

这是我的 TestRunner 的代码:

class DiscoveryDjangoTestSuiteRunner(DjangoTestSuiteRunner):
    """A test suite runner that uses unittest2 test discovery.
    It's better than the default django test runner, becouse it
    doesn't run Django tests and let you put your tests in different
    packages, modules and classes.

    To test everything in there:
        $ ./manage.py test

    To test a single package/module:

        $ ./manage.py test package
        $ ./manage.py test package.module

    To test a single class:

        $ ./manage.py test package.module.ClassName
    """
    def build_suite(self, test_labels, extra_tests=None, **kwargs):
        suite = None
        discovery_root = settings.TEST_DISCOVERY_ROOT
        if test_labels:
            # This is where I append the path
            suite = defaultTestLoader.loadTestsFromNames(test_labels)
            # if single named module has no tests, do discovery within it
            if not suite.countTestCases() and len(test_labels) == 1:
                suite = None
                discovery_root = import_module(test_labels[0]).__path__[0]

        if suite is None:
            suite = defaultTestLoader.discover(
                discovery_root,
                top_level_dir=settings.BASE_PATH,
                )

        if extra_tests:
            for test in extra_tests:
                suite.addTest(test)

        return reorder_suite(suite, (TestCase,))

【问题讨论】:

    标签: python django unit-testing testing django-testing


    【解决方案1】:

    在您继续为自定义TestRunner 投入更多时间之前,我绝对建议您查看django-nose

    django-nose 提供的自定义测试运行器实现了nose 的测试运行器,它非常灵活,并提供了很多运行测试的选项。它无缝覆盖默认的test 管理命令,并允许您在项目的settings 模块中配置默认​​测试选项。

    我真的推荐它有几个原因:

    • test 命令的选项已完整记录 (take look at the output)
    • nose 为test discovery 提供了很多方法
    • 您的同事可能已经是经验丰富的鼻子使用者了
    • 您不必自己编写TestRunner

    【讨论】:

    • 很好,谢谢。我一直在看鼻子。不过,我需要我的 PM 批准。
    【解决方案2】:

    您的 Python 导入层次结构植根于 project/src。因此,test_models 模块的正确 Python 导入路径是 project_name.test.some_app.test_models,这就是我希望作为测试标签传入的路径。

    但是您不喜欢每次要运行特定的测试模块时都输入project_name.test 前缀,因为您的所有测试都将位于那里。没关系:您选择引入一些隐含的非显而易见的行为以换取一些便利。为了实现这一点,您绝对不应该向sys.path 添加任何内容:Python 导入健全性的关键是将给定代码库的导入层次结构植根于一个且完全一致的位置;重叠的 sys.path 条目将导致问题,例如以不同名称重复导入同一模块。

    实际上,您想要的只是 UI 便利,在我看来,您展示的 test-label-munging 代码是实现这种便利的明显方式。你不喜欢硬编码 project_name.test 前缀,但它必须在某个地方硬编码:测试运行器不可能神奇地发现你想在测试标签前面加上 project_name.test。如果您希望 TestRunner 更通用,可以将其拉出到 BASE_TEST_MODULE 之类的设置中,并将该设置的值添加到每个测试标签中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-19
      • 2020-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多