Django manage.py:是否可以传递命令行参数(用于单元测试)?

25

在Django的 manage.py 脚本中,是否可以传递命令行参数,特别是用于单元测试? 例如,如果我执行以下操作:

manage.py test myapp -a do_this

在单元测试的setUp函数中,我能否接收到do_this的值?

P.S. @Martin询问测试中使用命令行参数的理由:

  • 一些复杂的测试需要很长时间,并不需要在每次提交之前运行。我想让它们变成可选的。

  • 我的测试用例偶尔会输出调试信息,这应该是可选的

  • 有时候我只是希望测试能疯狂地尝试更多的数据排列组合。

所有这些都可以通过命令行选项来方便地实现。有时候测试会更加广泛或冗长,否则就会很快结束。


无论是肯定还是否定,将参数添加到单元测试本身中是否更有意义呢?这确实是单元测试的主要用例之一——检查不同的边缘情况等。如果您为每个情况定义不同的测试函数,您将能够像manage.py test myapp.mytestcase一样单独调用它们。 - Martin B.
@Martin 说得好。我在答案中详细阐述了我的想法(附言)。 - user4150760
好的,那么我将创建诸如 testBasictestCrazy 等函数,并在每次提交时运行您需要的测试。我完全同意,在每次提交时运行一个大项目的整个测试套件可能会很烦人 - 这就是为什么您应该为每个提交创建一个新的测试,或者只选择与提交相关的测试。 - Martin B.
"@user4150760 - 对我来说"manage.py test myapp.mytestcase"不够用。有时候我想测试特定的客户端ID。那该怎么办?" - Berry Tsakala
6个回答

11
我刚刚遇到了这个问题,而且我想避免在命令行上设置环境变量。环境变量确实有效,但很难跟踪哪些变量有影响,如果您输错了其中一个变量,就不会有错误消息通知您。
为了解决这个问题,我使用了 argparse 从命令行参数中提取额外的参数。例如,我的 manage.py 文件现在看起来像这样:
#!/usr/bin/env python
import os
import sys
import argparse


if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")

    argv = sys.argv
    cmd = argv[1] if len(argv) > 1 else None
    if cmd in ['test']:  # limit the extra arguments to certain commands
        parser = argparse.ArgumentParser(add_help=False)
        parser.add_argument('--foo', default='bar')
        args, argv = parser.parse_known_args(argv)
        # We can save the argument as an environmental variable, in
        # which case it's to retrieve from within `project.settings`,
        os.environ['FOO'] = args.foo
        # or we can save the variable to settings directly if it
        # won't otherwise be overridden.
        from django.conf import settings
        settings.foo = args.foo

    from django.core.management import execute_from_command_line

    # parse_known_args strips the extra arguments from argv,
    # so we can safely pass it to Django.
    execute_from_command_line(argv)

argparse是一个非常好的库,具有许多功能。Python文档中有一篇很好的教程


10

我在我的项目中使用环境变量解决方案(仅适用于类Unix的shell)

berry$ myvar=myval ./manage.py test 

在您的模块中,使用以下方式读取此值:

os.environ.get('myvar')

7

Django允许从testrunner类添加自定义命令行选项。您可以创建默认testrunner类的子类并添加自己的选项,然后让Django使用您的自定义testrunner,如下所示。

例如,在您的Django项目目录中创建一个testrunner.py文件,其中包含以下内容:

from django.test.runner import DiscoverRunner

class TestRunner(DiscoverRunner):
    def __init__(self, option=None, **kwargs):
        super().__init__(**kwargs)

        print("Passed option: {}".format(option))

    @classmethod
    def add_arguments(cls, parser):
        DiscoverRunner.add_arguments(parser)

        parser.add_argument('-o', '--option', help='Example option')

这是一个测试运行程序,它派生自默认的运行程序(因此它的工作方式与默认值相同),但它会告诉Django添加一个额外的命令行选项(在add_arguments()类方法中),并在构造函数中处理这个额外选项的值。要使用这个新运行程序运行,请按以下方式传递它的名称:

./manage.py test --testrunner=testrunner.TestRunner -o foo

当然,您可以将此类放在任何其他位置,只要在命令行上传递完整的导入名称即可。
请注意,您必须使用--testrunner=foo,不能使用两个单独的参数(--testrunner foo),因为那样额外的参数不起作用。修复正在进行中:https://github.com/django/django/pull/10307 此示例仅打印选项值,但您可能需要以某种方式将其传递给测试用例。我找不到有关如何将选项传递给unittest测试用例的快速信息,但是您可以使用全局(模块级)变量或类变量来实现这一点(这不太可重入和优雅,但很容易且有效)。

4
作为替代 manage.py test -a do_this 的方法,你可以使用特定的设置文件
manage.py --settings=project.test_settings test

在这个文件中定义任何你想要的东西。
# test_setting.py
SPECIFIC_OPTION = "test"

# tests.py
from django.conf import settings
...
def setUp(self):
    if settings.SPECIFIC_OPTION:
        ....

如果您需要非常动态的选项,也许可以在test_settings.py中使用sys.argv,但这是一种非常不好的方法。

谢谢您提出这个解决方法。您提到:“您可以在test_settings.py中使用sys.argv,但这是一个非常不好的hack”,您能详细说明一下吗? - user4150760
2
你可以将任何参数传递给命令行:$ manage.py --settings=test_settings test some-specific-option,然后在 test_settings.py 中使用类似这样的代码:if sys.argv[-1] == "something": # do stuff (最后必须删除这个额外的参数:del sys.argv[-1])。 - erthalion
我并不觉得这有什么猥琐的地方。我已经为我的CI准备了一个单独的设置文件,只需在其中添加另一个变量即可。简单胜于复杂。 :) - Ojas Kale

4

在 @Matthijs 的答案之后,你可以类似于 DiscoveryRunner 处理 debug-mode 的方式来扩展 setup_test_environment 方法。 它会改变 settings.DEBUG 的值,通过导入 django.conf.settings 可以在你的测试中使用。但是你也可以添加自己的设置:

from django.test.runner import DiscoverRunner

class TestRunner(DiscoverRunner):
    def __init__(self, option=None, **kwargs):
        super().__init__(**kwargs)

        print("Passed option: {}".format(option))
        self.option = option

    @classmethod
    def add_arguments(cls, parser):
        DiscoverRunner.add_arguments(parser)

        parser.add_argument('-o', '--option', help='Example option')

    def setup_test_environment(self, **kwargs):
        super(TestRunner, self).setup_test_environment(**kwargs)
        settings.TEST_SETTINGS = {
            'option': self.option,
        }

在测试中,您可以通过设置选项轻松访问该选项。

from django.test import TestCase
from django.conf import settings


class MyTestCase(TestCase):

    def test_something(self):
        if settings.TEST_SETTINGS['option']:
            print("Do stuff")

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接