Làm cách nào tôi có thể gọi lệnh quản lý Django tùy chỉnh trực tiếp từ trình điều khiển thử nghiệm?


174

Tôi muốn viết một bài kiểm tra đơn vị cho lệnh Django Manage.py thực hiện thao tác phụ trợ trên bảng cơ sở dữ liệu. Làm thế nào tôi có thể gọi lệnh quản lý trực tiếp từ mã?

Tôi không muốn thực thi lệnh trên hệ điều hành của hệ điều hành từ tests.py vì tôi không thể sử dụng môi trường kiểm tra được thiết lập bằng cách sử dụng kiểm tra Manage.txt (cơ sở dữ liệu kiểm tra, hộp thư giả email kiểm tra, v.v.)

Câu trả lời:


312

Cách tốt nhất để kiểm tra những thứ như vậy - trích xuất chức năng cần thiết từ chính lệnh sang chức năng hoặc lớp độc lập. Nó giúp trừu tượng hóa từ "công cụ thực thi lệnh" và viết kiểm tra mà không cần thêm yêu cầu.

Nhưng nếu bạn vì một lý do nào đó không thể tách rời lệnh biểu mẫu logic, bạn có thể gọi nó từ bất kỳ mã nào bằng phương thức call_command như thế này:

from django.core.management import call_command

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

19
+1 để đặt logic có thể kiểm tra ở một nơi khác (phương thức mô hình? Phương thức quản lý? Hàm độc lập?) Để bạn không cần phải lộn xộn với máy móc call_command. Cũng làm cho các chức năng dễ dàng hơn để sử dụng lại.
Carl Meyer

36
Ngay cả khi bạn trích xuất logic, chức năng này vẫn hữu ích để kiểm tra hành vi cụ thể của lệnh của bạn, như các đối số được yêu cầu và để đảm bảo rằng nó gọi hàm phù thủy của thư viện là công việc thực sự.
Igor Sobreira

Đoạn mở đầu áp dụng cho bất kỳ tình huống biên. Di chuyển mã logic biz của riêng bạn ra khỏi mã bị ràng buộc để giao tiếp với một cái gì đó, chẳng hạn như người dùng. Tuy nhiên, nếu bạn viết dòng mã, nó có thể có lỗi, vì vậy các bài kiểm tra thực sự sẽ đạt được phía sau bất kỳ ranh giới nào.
Phlip

Tôi nghĩ rằng điều này vẫn hữu ích cho một cái gì đó như call_command('check'), để đảm bảo kiểm tra hệ thống được thông qua, trong một thử nghiệm.
Adam Barnes

22

Thay vì thực hiện thủ thuật call_command, bạn có thể chạy tác vụ của mình bằng cách thực hiện:

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
Tại sao bạn sẽ làm điều này khi call_command cũng cung cấp để chụp stdin, stdout, stderr? Và khi tài liệu chỉ định đúng cách để làm điều này?
thuyền viên

17
Đó là một câu hỏi cực kỳ tốt. Ba năm trước có lẽ tôi đã có câu trả lời cho bạn;)
Nate

1
Ditto Nate - khi câu trả lời của anh ấy là những gì tôi tìm thấy cách đây một năm rưỡi - tôi chỉ đơn thuần dựa trên nó ...
Danny Staple

2
Đào bài, nhưng hôm nay điều này đã giúp tôi: Tôi không phải lúc nào cũng sử dụng tất cả các ứng dụng của cơ sở mã của mình (tùy thuộc vào trang Django được sử dụng) và call_commandcần tải ứng dụng đã thử nghiệm INSTALLED_APPS. Giữa việc phải tải ứng dụng chỉ cho mục đích thử nghiệm và sử dụng cái này, tôi đã chọn cái này.
Mickaël

call_commandcó lẽ là điều mà hầu hết mọi người nên thử đầu tiên. Câu trả lời này đã giúp tôi giải quyết một vấn đề mà tôi cần chuyển tên bảng unicode cho inspectdblệnh. python / bash đang diễn giải dòng lệnh args là ascii, và điều đó đã đánh bom get_table_descriptioncuộc gọi sâu trong django.
bigh_29

17

đoạn mã sau:

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

... bằng với các lệnh sau được nhập trong terminal:

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

Xem các lệnh quản lý đang chạy từ tài liệu django .


14

Các tài liệu Django trên call_command không đề cập đến đó outphải được chuyển đến sys.stdout. Mã ví dụ nên đọc:

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

Dựa trên câu trả lời của Nate tôi có điều này:

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

Sử dụng:

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

Ưu điểm ở đây là nếu bạn đã sử dụng các tùy chọn bổ sung và OptPude, điều này sẽ sắp xếp cho bạn. Nó không hoàn hảo - và nó chưa xuất ra đường ống - nhưng nó sẽ sử dụng cơ sở dữ liệu thử nghiệm. Sau đó bạn có thể kiểm tra hiệu ứng cơ sở dữ liệu.

Tôi chắc chắn việc sử dụng mô-đun giả Micheal Foords và cũng sử dụng lại thiết bị xuất chuẩn trong thời gian thử nghiệm có nghĩa là bạn cũng có thể nhận được thêm một số kỹ thuật này - kiểm tra đầu ra, điều kiện thoát, v.v.


4
Tại sao bạn lại gặp phải tất cả những rắc rối này thay vì chỉ sử dụng call_command?
thuyền viên
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.