เอกสารขึ้นฉ่ายกล่าวถึงการทดสอบคื่นฉ่ายภายใน Djangoแต่ไม่ได้อธิบายวิธีทดสอบงานขึ้นฉ่ายหากคุณไม่ได้ใช้ Django คุณจะทำอย่างไร?
เอกสารขึ้นฉ่ายกล่าวถึงการทดสอบคื่นฉ่ายภายใน Djangoแต่ไม่ได้อธิบายวิธีทดสอบงานขึ้นฉ่ายหากคุณไม่ได้ใช้ Django คุณจะทำอย่างไร?
คำตอบ:
เป็นไปได้ที่จะทดสอบงานพร้อมกันโดยใช้ lib ที่ไม่เหมาะสมที่สุด ปกติฉันทำแบบทดสอบ 2 ครั้งเมื่อทำงานกับคื่นช่าย อันแรก (ตามที่ฉันกำลังแนะนำให้ร้อง) เป็นแบบซิงโครนัสอย่างสมบูรณ์และควรเป็นสิ่งที่ทำให้แน่ใจว่าอัลกอริทึมทำในสิ่งที่ควรทำ เซสชันที่สองใช้ทั้งระบบ (รวมถึงโบรกเกอร์) และตรวจสอบให้แน่ใจว่าฉันไม่มีปัญหาการทำให้เป็นอนุกรมหรือปัญหาการกระจายอื่น ๆ การสื่อสาร
ดังนั้น:
from celery import Celery
celery = Celery()
@celery.task
def add(x, y):
return x + y
และการทดสอบของคุณ:
from nose.tools import eq_
def test_add_task():
rst = add.apply(args=(4, 4)).get()
eq_(rst, 8)
หวังว่าจะช่วยได้!
celery.loader.import_default_modules()
ในการสั่งซื้อเพื่อหลีกเลี่ยงการนี้ผมขอแนะนำให้คุณโทร
ฉันใช้สิ่งนี้:
with mock.patch('celeryconfig.CELERY_ALWAYS_EAGER', True, create=True):
...
เอกสาร: http://docs.celeryproject.org/en/3.1/configuration.html#celery-always-eager
CELERY_ALWAYS_EAGER ช่วยให้คุณรันงานของคุณแบบซิงโครนัสและคุณไม่จำเป็นต้องใช้เซิร์ฟเวอร์ขึ้นฉ่าย
ImportError: No module named celeryconfig
แล้ว
celeryconfig.py
มีอยู่ในแพ็คเกจ ดูdocs.celeryproject.org/en/latest/getting-started/… .
add
จากคำถามของ OP ภายในTestCase
ชั้นเรียนได้ไหม
CELERY_TASK_ALWAYS_EAGER
สำหรับการทดสอบหน่วย
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
สำหรับผู้ที่ขึ้นฉ่าย 4 ได้แก่ :
@override_settings(CELERY_TASK_ALWAYS_EAGER=True)
เนื่องจากชื่อการตั้งค่ามีการเปลี่ยนแปลงและจำเป็นต้องอัปเดตหากคุณเลือกที่จะอัปเกรดโปรดดู
สำหรับCelery 3.0วิธีหนึ่งในการตั้งค่าCELERY_ALWAYS_EAGER
ในDjangoคือ:
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())
ตั้งแต่คื่นฉ่ายv4.0 , ติดตั้ง py.test จะจัดให้มีการเริ่มต้นของผู้ปฏิบัติงานคื่นฉ่ายเพียงสำหรับการทดสอบและมีการปิดตัวลงเมื่อทำ:
def test_myfunc_is_executed(celery_session_worker):
# celery_session_worker: <Worker: gen93553@gnpill.local (running)>
assert myfunc.delay().wait(3)
ในบรรดาการแข่งขันอื่น ๆ ที่อธิบายไว้ในhttp://docs.celeryproject.org/en/latest/userguide/testing.html#py-testคุณสามารถเปลี่ยนตัวเลือกเริ่มต้นขึ้นฉ่ายได้โดยการกำหนดcelery_config
ฟิกซ์เจอร์ใหม่ด้วยวิธีนี้:
@pytest.fixture(scope='session')
def celery_config():
return {
'accept_content': ['json', 'pickle'],
'result_serializer': 'pickle',
}
โดยค่าเริ่มต้นผู้ทดสอบจะใช้นายหน้าในหน่วยความจำและแบ็กเอนด์ผลลัพธ์ ไม่จำเป็นต้องใช้ Redis ในพื้นที่หรือ RabbitMQ หากไม่ได้ทดสอบคุณสมบัติเฉพาะ
การอ้างอิง โดยใช้ pytest
def test_add(celery_worker):
mytask.delay()
หากคุณใช้ขวดให้ตั้งค่าการกำหนดค่าแอป
CELERY_BROKER_URL = 'memory://'
CELERY_RESULT_BACKEND = 'cache+memory://'
และใน 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
ในกรณีของฉัน (และฉันถือว่าคนอื่น ๆ อีกมากมาย) สิ่งที่ฉันต้องการคือการทดสอบตรรกะภายในของงานโดยใช้ pytest
TL; DR; จบลงด้วยการเยาะเย้ยทุกสิ่งออกไป (ตัวเลือกที่ 2 )
ตัวอย่างการใช้งานกรณี :
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'
แต่เนื่องจากshared_task
มัณฑนากรใช้ตรรกะภายในคื่นฉ่ายจำนวนมากจึงไม่ได้เป็นการทดสอบหน่วย
ดังนั้นสำหรับฉันมี 2 ตัวเลือก:
ตัวเลือกที่ 1: แยกตรรกะภายใน
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);
สิ่งนี้ดูแปลกมากและนอกเหนือจากการทำให้อ่านได้น้อยลงแล้วจำเป็นต้องแยกและส่งผ่านแอตทริบิวต์ที่เป็นส่วนหนึ่งของคำขอด้วยตนเองเช่นtask_id
ในกรณีที่คุณต้องการซึ่งทำให้ตรรกะบริสุทธิ์น้อยลง
ตัวเลือกที่ 2: ล้อเลียน
เยาะเย้ยขึ้นฉ่ายภายใน
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()
ซึ่งจะทำให้ฉันสามารถเยาะเย้ยออบเจ็กต์คำขอได้ (อีกครั้งในกรณีที่คุณต้องการสิ่งต่างๆจากคำขอเช่นรหัสหรือตัวนับการลองใหม่
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'
วิธีการแก้ปัญหานี้เป็นแบบแมนนวลมากขึ้น แต่มันให้การควบคุมที่ฉันต้องใช้ในการทดสอบหน่วยจริงโดยไม่ต้องทำซ้ำด้วยตัวเองและไม่สูญเสียขอบเขตขึ้นฉ่าย