如何直接从测试驱动程序调用自定义Django manage.py命令?


174

我想为在数据库表上执行后端操作的Django manage.py命令编写单元测试。如何直接从代码中调用管理命令?

我不想从tests.py在操作系统的外壳上执行命令,因为我无法使用通过manage.py test设置的测试环境(测试数据库,测试虚拟电子邮件发件箱等)。

Answers:


312

测试这些事情的最佳方法-从命令本身提取所需的功能到独立的函数或类。它有助于从“命令执行内容”中提取内容并编写测试,而无其他要求。

但是,如果由于某种原因无法将逻辑形式的命令解耦,则可以使用如下的call_command方法从任何代码中调用它:

from django.core.management import call_command

call_command('my_command', 'foo', bar='baz')

19
+1将可测试的逻辑放在其他地方(模型方法?管理器方法?独立函数?),因此您完全不需要弄乱call_command机制。也使功能更易于重用。
卡尔·迈尔

35
即使您提取逻辑,此功能对于测试特定于命令的行为(如必需的参数)以及确保它调用库函数的实际作用仍然有用。
伊戈尔·索布雷拉

开篇适用于任何边界情况。将您自己的biz逻辑代码从受限于与诸如用户之类的接口的代码中移出。但是,如果您编写代码行,则可能会出现错误,因此测试确实应该达到任何边界。
Phlip

我认为这对于诸如之类的东西仍然很有用call_command('check'),以确保在测试中通过系统检查。
亚当·巴恩斯

22

除了执行call_command技巧外,您还可以执行以下操作来运行任务:

from myapp.management.commands import my_management_task
cmd = my_management_task.Command()
opts = {} # kwargs for your command -- lets you override stuff for testing...
cmd.handle_noargs(**opts)

10
当call_command还提供捕获stdin,stdout,stderr时,为什么要这样做?当文档指定执行此操作的正确方法时?
boatcoder 2014年

16
这是一个非常好的问题。三年前,也许我会为您提供一个答案;)
Nate 2014年

1
同上的内特-当他的答案是一年半以前我发现的时候-我只是在此基础上...
丹尼·史泰博

2
进行后期挖掘,但今天对我有所帮助:我并不总是使用我的代码库的所有应用程序(取决于所使用的Django站点),并且call_command需要将经过测试的应用程序加载到中INSTALLED_APPS。在仅出于测试目的而加载应用程序和使用它之间,我选择了此方法。
的Mickaël

call_command这可能是大多数人应该首先尝试的。这个答案帮助我解决了需要将unicode表名传递给inspectdb命令的问题。python / bash将命令行参数args解释为ascii,这get_table_description在django的深处轰炸了这一电话。
bigh_29 '19

17

以下代码:

from django.core.management import call_command
call_command('collectstatic', verbosity=3, interactive=False)
call_command('migrate', 'myapp', verbosity=3, interactive=False)

...等于在终端中键入以下命令:

$ ./manage.py collectstatic --noinput -v 3
$ ./manage.py migrate myapp --noinput -v 3

请参阅从django docs运行管理命令


14

关于call_commandDjango文档未提及out必须将其重定向到sys.stdout。示例代码应显示为:

from django.core.management import call_command
from django.test import TestCase
from django.utils.six import StringIO
import sys

class ClosepollTest(TestCase):
    def test_command_output(self):
        out = StringIO()
        sys.stdout = out
        call_command('closepoll', stdout=out)
        self.assertIn('Expected output', out.getvalue())

1

基于Nate的答案,我有以下内容:

def make_test_wrapper_for(command_module):
    def _run_cmd_with(*args):
        """Run the possibly_add_alert command with the supplied arguments"""
        cmd = command_module.Command()
        (opts, args) = OptionParser(option_list=cmd.option_list).parse_args(list(args))
        cmd.handle(*args, **vars(opts))
    return _run_cmd_with

用法:

from myapp.management import mycommand
cmd_runner = make_test_wrapper_for(mycommand)
cmd_runner("foo", "bar")

这样做的好处是,如果您使用了其他选项和OptParse,它将为您解决问题。它不是很完美-并且还没有管道输出-但它将使用测试数据库。然后,您可以测试数据库效果。

我确信使用Micheal Foords模拟模块并在测试过程中重新连接stdout意味着您也可以从该技术中获得更多帮助-测试输出,退出条件等。


4
为什么您会遇到所有这些麻烦,而不仅仅是使用call_command?
boatcoder 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.