Kereviz belgeleri Django'da Kereviz testinden bahseder, ancak Django kullanmıyorsanız bir Kereviz görevinin nasıl test edileceğini açıklamaz. Bunu nasıl yapıyorsun?
Kereviz belgeleri Django'da Kereviz testinden bahseder, ancak Django kullanmıyorsanız bir Kereviz görevinin nasıl test edileceğini açıklamaz. Bunu nasıl yapıyorsun?
Yanıtlar:
Orada herhangi bir unittest lib kullanarak görevleri eşzamanlı olarak test etmek mümkündür. Kereviz işleriyle çalışırken normalde 2 farklı test seansı yaparım. İlki (aşağıda önerdiğim gibi) tamamen eşzamanlıdır ve algoritmanın yapması gerekeni yaptığından emin olmalıdır. İkinci oturum tüm sistemi (komisyoncu dahil) kullanır ve serileştirme sorunları veya başka herhangi bir dağıtım, iletişim sorunu yaşamadığımdan emin olur.
Yani:
from celery import Celery
celery = Celery()
@celery.task
def add(x, y):
return x + y
Ve testin:
from nose.tools import eq_
def test_add_task():
rst = add.apply(args=(4, 4)).get()
eq_(rst, 8)
Umarım yardımcı olur!
celery.loader.import_default_modules()
.
Bunu kullanıyorum:
with mock.patch('celeryconfig.CELERY_ALWAYS_EAGER', True, create=True):
...
Dokümanlar: http://docs.celeryproject.org/en/3.1/configuration.html#celery-always-eager
CELERY_ALWAYS_EAGER, görevinizi eşzamanlı çalıştırmanıza izin verir ve bir kereviz sunucusuna ihtiyacınız yoktur.
ImportError: No module named celeryconfig
.
celeryconfig.py
birinin paketinde bulunduğunu varsaydığına inanıyorum . Docs.celeryproject.org/en/latest/getting-started/… bakın .
add
bir TestCase
sınıf içinde OP'nin sorusundan nasıl görev başlatılacağına dair tam bir örnek verebilir misiniz ?
CELERY_TASK_ALWAYS_EAGER
birim testi için kullanılmasını caydırdığı yönünde bir yorum bıraktım .
Tam olarak neyi test etmek istediğinize bağlı.
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
Kereviz 4'tekiler için:
@override_settings(CELERY_TASK_ALWAYS_EAGER=True)
Ayar adları değiştirildiğinden ve yükseltmeyi seçerseniz güncellenmesi gerektiğinden, bkz.
İtibariyle Kereviz 3.0 , ayarlamak için bir yol CELERY_ALWAYS_EAGER
içinde Django geçerli:
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())
Kereviz v4.0'dan beri , py.test armatürleri bir kereviz işçisini sadece test için başlatmak için sağlanmıştır ve bittiğinde kapatılır:
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 adresinde açıklanan diğer fikstürler arasında , celery_config
fikstürü şu şekilde yeniden tanımlayarak kereviz varsayılan seçeneklerini değiştirebilirsiniz :
@pytest.fixture(scope='session')
def celery_config():
return {
'accept_content': ['json', 'pickle'],
'result_serializer': 'pickle',
}
Varsayılan olarak, test çalışanı bir bellek içi aracı ve sonuç arka ucu kullanır. Belirli özellikleri test etmiyorsanız yerel Redis veya RabbitMQ kullanmaya gerek yoktur.
pytest kullanarak referans .
def test_add(celery_worker):
mytask.delay()
flask kullanıyorsanız, uygulama yapılandırmasını ayarlayın
CELERY_BROKER_URL = 'memory://'
CELERY_RESULT_BACKEND = 'cache+memory://'
ve 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
Benim durumumda (ve diğerlerinin çoğunu varsayıyorum), tek istediğim bir görevin iç mantığını pytest kullanarak test etmekti.
TL; DR; her şeyi alay konusu yaptı ( SEÇENEK 2 )
Örnek Kullanım Örneği :
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'
ancak, shared_task
dekoratör çok fazla kereviz iç mantığı yaptığından, bu aslında bir birim testi değildir.
Yani benim için 2 seçenek vardı:
SEÇENEK 1: Ayrı dahili mantık
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);
Bu çok tuhaf görünüyor ve daha az okunabilir hale getirmenin yanı sıra, isteğin parçası olan öznitelikleri, örneğin task_id
ihtiyacınız olduğunda, mantığı daha az saf hale getiren manuel olarak ayıklayıp geçirmeyi gerektirir .
SEÇENEK 2:
kereviz iç malzemeleriyle alay eden taklitler
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()
bu da istek nesnesiyle alay etmeme izin veriyor (yine, istekten, id veya yeniden deneme sayacı gibi şeylere ihtiyacınız olması durumunda.
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'
Bu çözüm çok daha manuel, ancak bana kendimi tekrar etmeden ve kereviz kapsamını kaybetmeden aslında birim testi yapmak için ihtiyacım olan kontrolü veriyor .