테스트 드라이버에서 직접 사용자 지정 Django manage.py 명령을 호출하려면 어떻게해야합니까?


174

데이터베이스 테이블에서 백엔드 작업을 수행하는 Django manage.py 명령에 대한 단위 테스트를 작성하려고합니다. 코드에서 직접 관리 명령을 어떻게 호출합니까?

manage.py test (테스트 데이터베이스, 테스트 더미 이메일 발신 함 등)를 사용하여 설정된 테스트 환경을 사용할 수 없기 때문에 tests.py에서 운영 체제 쉘에서 명령을 실행하고 싶지 않습니다.

답변:


312

이러한 것들을 테스트하는 가장 좋은 방법-필요한 기능을 명령 자체에서 독립형 함수 또는 클래스로 추출하십시오. "명령 실행 항목"에서 추상화하고 추가 요구 사항없이 테스트를 작성하는 데 도움이됩니다.

그러나 어떤 이유로 논리 양식 명령을 분리 할 수없는 경우 다음 과 같이 call_command 메소드를 사용하여 모든 코드에서 호출 할 수 있습니다 .

from django.core.management import call_command

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

19
+1 테스트 가능한 로직을 다른 곳에두면 (모델 방법? 관리자 방법? 독립형 함수?) call_command 기계를 전혀 망칠 필요가 없습니다. 또한 기능을보다 쉽게 ​​재사용 할 수 있습니다.
Carl Meyer

36
논리를 추출하더라도이 함수는 필수 인수와 같은 명령 특정 동작을 테스트하고 라이브러리 함수를 호출하여 실제 작업을 수행하는지 확인하는 데 여전히 유용합니다.
Igor Sobreira

시작 단락은 모든 경계 상황에 적용됩니다 . 비즈 로직 코드를 사용자와 같은 무언가와 인터페이스하도록 제한된 코드 밖으로 이동하십시오. 그러나 코드 줄을 작성하면 버그가 생길 수 있으므로 테스트는 실제로 경계를 넘어서야합니다.
Phlip

나는 이것이 call_command('check')테스트에서 시스템 검사가 통과되도록하는 데 여전히 유용하다고 생각합니다 .
Adam Barnes

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 캡처를 제공 할 때 왜이 작업을 수행합니까? 그리고 문서가 올바른 방법을 명시 할 때?
보트 코더

17
그것은 매우 좋은 질문입니다. 3 년 전 아마 당신에게 답이 있었을 것입니다;)
Nate

1
Ditto Nate – 그의 대답이 1 년 반 전에 찾은 것이었을 때 – 나는 단지 그것에 기초를
Danny Staple

2
포스트 파기, 그러나 이것은 오늘 나에게 도움이되었습니다 : 항상 Django 사이트에 따라 코드베이스의 모든 응용 프로그램을 사용하고 있지는 않으며 call_command테스트 된 응용 프로그램을에로드해야합니다 INSTALLED_APPS. 테스트 목적으로 앱을로드하고이를 사용하는 사이에서 이것을 선택했습니다.
Mickaël

call_command아마도 대부분의 사람들이 먼저 시도해야 할 것입니다. 이 답변은 유니 코드 테이블 이름을 inspectdb명령 에 전달 해야하는 문제를 해결하는 데 도움이되었습니다 . python / bash는 커맨드 라인 인수를 ASCII로 해석하고 있었고 get_table_description장고 에서 전화를 폭파했습니다 .
bigh_29

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를 사용하는 대신 왜이 모든 문제를 해결하겠습니까?
보트 코더
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.