ฉันจะเรียกคำสั่ง Django Manage.py ที่กำหนดเองได้โดยตรงจากไดรเวอร์ทดสอบได้อย่างไร


174

ฉันต้องการเขียนการทดสอบหน่วยสำหรับคำสั่ง Django Manage.py ที่ทำหน้าที่แบ็คเอนด์บนตารางฐานข้อมูล ฉันจะเรียกใช้คำสั่งการจัดการโดยตรงจากรหัสได้อย่างไร

ฉันไม่ต้องการรันคำสั่งบนเชลล์ของระบบปฏิบัติการจาก tests.py เพราะฉันไม่สามารถใช้สภาพแวดล้อมการทดสอบที่ตั้งค่าโดยใช้การทดสอบ Manage.py (ฐานข้อมูลทดสอบการทดสอบกล่องจดหมายออกอีเมล ฯลฯ )

คำตอบ:


312

วิธีที่ดีที่สุดในการทดสอบสิ่งต่าง ๆ - แยกฟังก์ชันการทำงานที่จำเป็นจากคำสั่งของตัวเองไปยังฟังก์ชั่นแบบสแตนด์อโลนหรือคลาส ช่วยในการสรุปจาก "command execution stuff" และการเขียนการทดสอบโดยไม่มีข้อกำหนดเพิ่มเติม

แต่ถ้าคุณด้วยเหตุผลบางอย่างไม่สามารถแยกคำสั่งตรรกะแบบฟอร์มคุณสามารถโทรจากรหัสใด ๆ โดยใช้วิธีการcall_commandเช่นนี้

from django.core.management import call_command

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

19
+1 เพื่อวางลอจิกที่สามารถทดสอบได้ที่อื่น (เมธอด model? manager เมธอดสแตนด์อะโลน) ดังนั้นคุณไม่จำเป็นต้องยุ่งกับ call_command เครื่องจักรเลย ยังทำให้การใช้งานง่ายขึ้น
Carl Meyer

36
แม้ว่าคุณจะแยกลอจิกฟังก์ชั่นนี้ยังคงมีประโยชน์ในการทดสอบการทำงานเฉพาะคำสั่งของคุณเช่นข้อโต้แย้งที่จำเป็นและเพื่อให้แน่ใจว่ามันเรียกแม่มดฟังก์ชั่นห้องสมุดของคุณทำงานจริง
Igor Sobreira

ย่อหน้าเปิดใช้กับสถานการณ์ขอบเขตใด ๆ ย้ายโค้ดลอจิก biz ของคุณเองออกจากโค้ดที่ถูก จำกัด ให้ใช้กับบางสิ่งเช่นผู้ใช้ อย่างไรก็ตามหากคุณเขียนบรรทัดของรหัสมันอาจมีข้อผิดพลาดดังนั้นการทดสอบควรถึงหลังขอบเขตใด ๆ
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? และเมื่อเอกสารระบุวิธีการที่ถูกต้องในการทำเช่นนี้?
boatcoder

17
นั่นเป็นคำถามที่ดีมาก สามปีที่ผ่านมาบางทีฉันอาจจะมีคำตอบสำหรับคุณ;)
เนต

1
Ditto Nate - เมื่อคำตอบของเขาคือสิ่งที่ฉันพบเมื่อปีครึ่งที่แล้ว - ฉันเพิ่งสร้างมัน ...
Danny Staple

2
โพสต์ขุด แต่วันนี้ช่วยให้ฉัน: ฉันไม่เคยใช้ทุกการใช้งานของ codebase ของฉัน (ขึ้นอยู่ของเว็บไซต์ Django ใช้) และความต้องการใช้การทดสอบที่จะโหลดในcall_command INSTALLED_APPSระหว่างการโหลดแอพเพื่อการทดสอบและการใช้งานนี้ฉันเลือกมัน
Mickaël

call_commandอาจเป็นสิ่งที่คนส่วนใหญ่ควรลองก่อน คำตอบนี้ช่วยฉันในการแก้ปัญหาที่ฉันต้องการส่งชื่อตาราง Unicode ให้กับinspectdbคำสั่ง python / bash กำลังตีความบรรทัดคำสั่งเป็น ascii และนั่นเป็นการวางget_table_descriptionสายที่ลึกลงไปใน django
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)

... เท่ากับคำสั่งต่อไปนี้พิมพ์ใน terminal:

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

ดูใช้คำสั่งการจัดการเอกสารจาก Django


14

เอกสาร 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

การสร้างคำตอบของเนทฉันมีสิ่งนี้:

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.