Tài liệu về Celery đề cập đến việc kiểm tra Celery trong Django nhưng không giải thích cách kiểm tra tác vụ Celery nếu bạn không sử dụng Django. Làm thế nào để bạn làm điều này?
Tài liệu về Celery đề cập đến việc kiểm tra Celery trong Django nhưng không giải thích cách kiểm tra tác vụ Celery nếu bạn không sử dụng Django. Làm thế nào để bạn làm điều này?
Câu trả lời:
Có thể kiểm tra đồng bộ các tác vụ bằng cách sử dụng bất kỳ lib nào mới nhất hiện có. Tôi bình thường thực hiện 2 phiên kiểm tra khác nhau khi làm việc với các tác vụ cần tây. Cái đầu tiên (như tôi đang đề xuất ở bên dưới) là hoàn toàn đồng bộ và phải là cái đảm bảo thuật toán thực hiện những gì nó phải làm. Phiên thứ hai sử dụng toàn bộ hệ thống (bao gồm cả nhà môi giới) và đảm bảo rằng tôi không gặp vấn đề tuần tự hóa hoặc bất kỳ vấn đề phân phối, giao tiếp nào khác.
Vì thế:
from celery import Celery
celery = Celery()
@celery.task
def add(x, y):
return x + y
Và bài kiểm tra của bạn:
from nose.tools import eq_
def test_add_task():
rst = add.apply(args=(4, 4)).get()
eq_(rst, 8)
Hy vọng rằng sẽ giúp!
celery.loader.import_default_modules()
.
Tôi sử dụng cái này:
with mock.patch('celeryconfig.CELERY_ALWAYS_EAGER', True, create=True):
...
Tài liệu: http://docs.celeryproject.org/en/3.1/configuration.html#celery-always-eager
CELERY_ALWAYS_EAGER cho phép bạn chạy đồng bộ tác vụ của mình và bạn không cần máy chủ cần tây.
ImportError: No module named celeryconfig
.
celeryconfig.py
tồn tại trong gói của một người. Xem docs.celeryproject.org/en/latest/getting-started/… .
add
từ câu hỏi của OP trong một TestCase
lớp không?
CELERY_TASK_ALWAYS_EAGER
cho thử nghiệm đơn vị.
Phụ thuộc vào chính xác những gì bạn muốn thử nghiệm.
import unittest
from myproject.myapp import celeryapp
class TestMyCeleryWorker(unittest.TestCase):
def setUp(self):
celeryapp.conf.update(CELERY_ALWAYS_EAGER=True)
# conftest.py
from myproject.myapp import celeryapp
@pytest.fixture(scope='module')
def celery_app(request):
celeryapp.conf.update(CELERY_ALWAYS_EAGER=True)
return celeryapp
# test_tasks.py
def test_some_task(celery_app):
...
from celery import current_app
def send_task(name, args=(), kwargs={}, **opts):
# https://github.com/celery/celery/issues/581
task = current_app.tasks[name]
return task.apply(args, kwargs, **opts)
current_app.send_task = send_task
Đối với những người trên Celery 4 đó là:
@override_settings(CELERY_TASK_ALWAYS_EAGER=True)
Vì tên cài đặt đã được thay đổi và cần cập nhật nếu bạn chọn nâng cấp, hãy xem
Kể từ Celery 3.0 , một cách để thiết lập CELERY_ALWAYS_EAGER
trong Django là:
from django.test import TestCase, override_settings
from .foo import foo_celery_task
class MyTest(TestCase):
@override_settings(CELERY_ALWAYS_EAGER=True)
def test_foo(self):
self.assertTrue(foo_celery_task.delay())
Kể từ Celery v4.0 , đồ đạc py.test được cung cấp để bắt đầu một công nhân cần tây chỉ để thử nghiệm và sẽ tắt khi hoàn thành:
def test_myfunc_is_executed(celery_session_worker):
# celery_session_worker: <Worker: gen93553@gnpill.local (running)>
assert myfunc.delay().wait(3)
Trong số các đồ đạc khác được mô tả trên http://docs.celeryproject.org/en/latest/userguide/testing.html#py-test , bạn có thể thay đổi các tùy chọn mặc định của cần tây bằng cách xác định lại đồ dùng celery_config
theo cách này:
@pytest.fixture(scope='session')
def celery_config():
return {
'accept_content': ['json', 'pickle'],
'result_serializer': 'pickle',
}
Theo mặc định, test worker sử dụng một trình môi giới trong bộ nhớ và phụ trợ kết quả. Không cần sử dụng Redis hoặc RabbitMQ cục bộ nếu không thử nghiệm các tính năng cụ thể.
tham khảo bằng cách sử dụng pytest.
def test_add(celery_worker):
mytask.delay()
nếu bạn sử dụng flask, hãy đặt cấu hình ứng dụng
CELERY_BROKER_URL = 'memory://'
CELERY_RESULT_BACKEND = 'cache+memory://'
và trong conftest.py
@pytest.fixture
def app():
yield app # Your actual Flask application
@pytest.fixture
def celery_app(app):
from celery.contrib.testing import tasks # need it
yield celery_app # Your actual Flask-Celery application
Trong trường hợp của tôi (và tôi giả sử nhiều người khác), tất cả những gì tôi muốn là kiểm tra logic bên trong của một nhiệm vụ bằng cách sử dụng pytest.
TL; DR; kết thúc bằng việc chế giễu mọi thứ ( TÙY CHỌN 2 )
Trường hợp sử dụng mẫu :
proj/tasks.py
@shared_task(bind=True)
def add_task(self, a, b):
return a+b;
tests/test_tasks.py
from proj import add_task
def test_add():
assert add_task(1, 2) == 3, '1 + 2 should equal 3'
nhưng kể từ khi shared_task
decorator thực hiện rất nhiều logic bên trong cần tây, nên nó không thực sự là một bài kiểm tra đơn vị.
Vì vậy, đối với tôi, có 2 lựa chọn:
TÙY CHỌN 1: Logic nội bộ riêng biệt
proj/tasks_logic.py
def internal_add(a, b):
return a + b;
proj/tasks.py
from .tasks_logic import internal_add
@shared_task(bind=True)
def add_task(self, a, b):
return internal_add(a, b);
Điều này trông rất kỳ lạ và ngoài việc làm cho nó khó đọc hơn, nó yêu cầu trích xuất và chuyển các thuộc tính là một phần của yêu cầu theo cách thủ công, ví dụ như task_id
trong trường hợp bạn cần, điều này làm cho logic kém thuần túy hơn.
LỰA CHỌN 2: Chế nhạo chế
giễu nội tạng cần tây
tests/__init__.py
# noinspection PyUnresolvedReferences
from celery import shared_task
from mock import patch
def mock_signature(**kwargs):
return {}
def mocked_shared_task(*decorator_args, **decorator_kwargs):
def mocked_shared_decorator(func):
func.signature = func.si = func.s = mock_signature
return func
return mocked_shared_decorator
patch('celery.shared_task', mocked_shared_task).start()
sau đó cho phép tôi mô phỏng đối tượng yêu cầu (một lần nữa, trong trường hợp bạn cần những thứ từ yêu cầu, như id hoặc bộ đếm thử lại.
tests/test_tasks.py
from proj import add_task
class MockedRequest:
def __init__(self, id=None):
self.id = id or 1
class MockedTask:
def __init__(self, id=None):
self.request = MockedRequest(id=id)
def test_add():
mocked_task = MockedTask(id=3)
assert add_task(mocked_task, 1, 2) == 3, '1 + 2 should equal 3'
Giải pháp này thủ công hơn nhiều, nhưng nó cung cấp cho tôi sự kiểm soát mà tôi cần để thực sự kiểm tra đơn vị , không lặp lại chính mình và không làm mất phạm vi cần tây.