How can I call a custom Django manage.py command directly from a test driver?

后端 未结 5 1831
悲&欢浪女
悲&欢浪女 2020-11-29 17:30

I want to write a unit test for a Django manage.py command that does a backend operation on a database table. How would I invoke the management command directly from code?

相关标签:
5条回答
  • 2020-11-29 18:09

    the following code:

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

    ...is equal to the following commands typed in terminal:

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

    See running management commands from django docs.

    0 讨论(0)
  • 2020-11-29 18:15

    Building on Nate's answer I have this:

    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
    

    Usage:

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

    The advantage here being that if you've used additional options and OptParse, this will sort the out for you. It isn't quite perfect - and it doesn't pipe outputs yet - but it will use the test database. You can then test for database effects.

    I am sure use of Micheal Foords mock module and also rewiring stdout for the duration of a test would mean you could get some more out of this technique too - test the output, exit conditions etc.

    0 讨论(0)
  • 2020-11-29 18:21

    The Django documentation on the call_command fails to mention that out must be redirected to sys.stdout. The example code should read:

    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())
    
    0 讨论(0)
  • 2020-11-29 18:29

    The best way to test such things - extract needed functionality from command itself to standalone function or class. It helps to abstract from "command execution stuff" and write test without additional requirements.

    But if you by some reason cannot decouple logic form command you can call it from any code using call_command method like this:

    from django.core.management import call_command
    
    call_command('my_command', 'foo', bar='baz')
    
    0 讨论(0)
  • 2020-11-29 18:30

    Rather than do the call_command trick, you can run your task by doing:

    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)
    
    0 讨论(0)
提交回复
热议问题