Thử nghiệm đơn vị với django-celery?


82

Tôi đang cố gắng đưa ra một phương pháp thử nghiệm cho dự án django-celery của chúng tôi . Tôi đã đọc các ghi chú trong tài liệu , nhưng nó không cho tôi ý tưởng tốt về những gì thực sự phải làm. Tôi không lo lắng về việc thử nghiệm các tác vụ trong daemon thực tế, chỉ là chức năng của mã của tôi . Chủ yếu tôi đang tự hỏi:

  1. Làm cách nào chúng ta có thể bỏ qua task.delay()trong quá trình kiểm tra (Tôi đã thử cài đặt CELERY_ALWAYS_EAGER = Truenhưng không có gì khác biệt)?
  2. Làm cách nào để chúng tôi sử dụng cài đặt thử nghiệm được khuyến nghị (nếu đó là cách tốt nhất) mà không thực sự thay đổi cài đặt của chúng tôi.py?
  3. Chúng ta vẫn có thể sử dụng manage.py testhay chúng ta phải sử dụng một người chạy tùy chỉnh?

Nhìn chung, mọi gợi ý hoặc mẹo thử nghiệm với cần tây sẽ rất hữu ích.


1
ý bạn là CELERY_ALWAYS_EAGERgì không có gì khác biệt?
askol

Tôi vẫn gặp lỗi không thể liên hệ với Rabbitmq.
Jason Webb

Bạn có dấu vết? Tôi đoán có điều gì đó khác ngoài .delayviệc cố gắng thiết lập kết nối.
askol

11
Cài đặt BROKER_BACKEND=memorycó thể hữu ích trong trường hợp đó.
askol

Hỏi bạn đã đúng. BROKER_BACKEND=memoryđã sửa nó. Nếu bạn đặt nó như một câu trả lời, tôi sẽ đánh dấu nó đúng.
Jason Webb

Câu trả lời:



72

Tôi thích sử dụng trình trang trí override_settings trong các bài kiểm tra cần kết quả cần tây để hoàn thành.

from django.test import TestCase
from django.test.utils import override_settings
from myapp.tasks import mytask

class AddTestCase(TestCase):

    @override_settings(CELERY_EAGER_PROPAGATES_EXCEPTIONS=True,
                       CELERY_ALWAYS_EAGER=True,
                       BROKER_BACKEND='memory')
    def test_mytask(self):
        result = mytask.delay()
        self.assertTrue(result.successful())

Nếu bạn muốn áp dụng điều này cho tất cả các thử nghiệm, bạn có thể sử dụng trình chạy thử cần tây như được mô tả tại http://docs.celeryproject.org/en/2.5/django/unit-testing.html về cơ bản đặt các cài đặt này ngoại trừ ( BROKER_BACKEND = 'memory').

Trong phần cài đặt:

TEST_RUNNER = 'djcelery.contrib.test_runner.CeleryTestSuiteRunner'

Nhìn vào nguồn của CeleryTestSuiteRunner và nó khá rõ ràng điều gì đang xảy ra.


1
Điều này không hoạt động với celery 4, ngay cả khi trường được đổi tên từ đây
shadi

Hoạt động trên Cần tây 3.1. Tôi chỉ có các trường hợp thử nghiệm Celery của tôi kế thừa từ một lớp cha với trình trang trí này. Bằng cách đó, nó chỉ cần thiết ở một nơi và không cần phải kéo vào djcelery.
kontextify 19/09/18

1
Điều này hoạt động tốt trên Celery 4.4. và Django 2.2. Cách tiếp cận tốt nhất để chạy các bài kiểm tra đơn vị mà tôi đã xem cho đến nay.
Erik Kalkoken

18

Đây là một đoạn trích từ lớp cơ sở thử nghiệm của tôi khai ra apply_asyncphương thức và bản ghi cho các lệnh gọi đến nó (bao gồm cả Task.delay.) Nó hơi thô, nhưng nó được quản lý để phù hợp với nhu cầu của tôi trong vài tháng qua mà tôi đã sử dụng nó.

from django.test import TestCase
from celery.task.base import Task
# For recent versions, Task has been moved to celery.task.app:
# from celery.app.task import Task
# See http://docs.celeryproject.org/en/latest/reference/celery.app.task.html

class CeleryTestCaseBase(TestCase):

    def setUp(self):
        super(CeleryTestCaseBase, self).setUp()
        self.applied_tasks = []

        self.task_apply_async_orig = Task.apply_async

        @classmethod
        def new_apply_async(task_class, args=None, kwargs=None, **options):
            self.handle_apply_async(task_class, args, kwargs, **options)

        # monkey patch the regular apply_sync with our method
        Task.apply_async = new_apply_async

    def tearDown(self):
        super(CeleryTestCaseBase, self).tearDown()

        # Reset the monkey patch to the original method
        Task.apply_async = self.task_apply_async_orig

    def handle_apply_async(self, task_class, args=None, kwargs=None, **options):
        self.applied_tasks.append((task_class, tuple(args), kwargs))

    def assert_task_sent(self, task_class, *args, **kwargs):
        was_sent = any(task_class == task[0] and args == task[1] and kwargs == task[2]
                       for task in self.applied_tasks)
        self.assertTrue(was_sent, 'Task not called w/class %s and args %s' % (task_class, args))

    def assert_task_not_sent(self, task_class):
        was_sent = any(task_class == task[0] for task in self.applied_tasks)
        self.assertFalse(was_sent, 'Task was not expected to be called, but was.  Applied tasks: %s' %                 self.applied_tasks)

Đây là ví dụ "tuyệt vời" về cách bạn sử dụng nó trong các trường hợp thử nghiệm của mình:

mymodule.py

from my_tasks import SomeTask

def run_some_task(should_run):
    if should_run:
        SomeTask.delay(1, some_kwarg=2)

test_mymodule.py

class RunSomeTaskTest(CeleryTestCaseBase):
    def test_should_run(self):
        run_some_task(should_run=True)
        self.assert_task_sent(SomeTask, 1, some_kwarg=2)

    def test_should_not_run(self):
        run_some_task(should_run=False)
        self.assert_task_not_sent(SomeTask)

4

vì tôi vẫn thấy điều này xuất hiện trong kết quả tìm kiếm, cài đặt ghi đè với

TEST_RUNNER = 'djcelery.contrib.test_runner.CeleryTestSuiteRunner'

đã làm việc cho tôi theo Celery Docs


1

Đây là những gì tôi đã làm

Trong myapp.tasks.py tôi có:

from celery import shared_task

@shared_task()
def add(a, b):
    return a + b

Bên trong myapp.test_tasks.py tôi có:

from django.test import TestCase, override_settings
from myapp.tasks import add


class TasksTestCase(TestCase):

    def setUp(self):
        ...

    @override_settings(CELERY_TASK_ALWAYS_EAGER=True,CELERY_TASK_EAGER_PROPOGATES=True)
    def test_create_sections(self):
        result= add.delay(1,2)
        assert result.successful() == True
        assert result.get() == 3

0

Đối với tất cả mọi người đến đây vào năm 2019: hãy xem bài viết này bao gồm các chiến lược khác nhau, bao gồm cả việc gọi tác vụ một cách đồng bộ.

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.