Как я могу вызвать пользовательскую команду Django manage.py прямо из тестового драйвера?

124

Я хочу написать unit test для команды Django manage.py, которая выполняет бэкэнд-операцию в таблице базы данных. Как я могу вызвать команду управления непосредственно из кода?

Я не хочу выполнять команду на оболочке операционной системы из tests.py, потому что я не могу использовать тестовую среду, настроенную с помощью теста manage.py(тестовая база данных, тестовый фиктивный почтовый ящик и т.д.). )

Теги:
unit-testing

5 ответов

229
Лучший ответ

Лучший способ проверить такие вещи - извлечь необходимую функциональность из самой команды в автономную функцию или класс. Он помогает абстрагироваться от "командного исполнения" и писать тест без дополнительных требований.

Но если вы по какой-то причине не можете отделить логическую форму команды, вы можете вызвать ее из любого кода, подобного этому:

from django.core.management import call_command

call_command('my_command', 'foo', bar='baz')
  • 18
    +1 к помещению тестируемой логики в другое место (метод модели? Метод менеджера? Автономная функция?), Чтобы вам вообще не нужно было связываться с механизмом call_command. Также облегчает повторное использование функциональности.
  • 32
    Даже если вы извлекаете логику, эта функция по-прежнему полезна для проверки поведения вашей команды, например, необходимых аргументов, и чтобы убедиться, что она вызывает вашу библиотечную функцию, которая действительно работает.
Показать ещё 2 комментария
18

Вместо трюка 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)
  • 8
    Зачем вам это делать, когда call_command также обеспечивает захват stdin, stdout, stderr? А когда в документации указан правильный способ сделать это?
  • 15
    Это очень хороший вопрос. Три года назад, возможно, у меня был бы ответ для вас;)
Показать ещё 3 комментария
12

следующий код:

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.

4

Документация Django в call_command не упоминает, что 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 на время теста означало бы, что вы могли бы получить еще больше от этой техники - проверьте выходные данные, условия выхода и т.д.

  • 3
    Зачем вам идти на все эти неприятности вместо того, чтобы просто использовать call_command?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню