Bir Kereviz görevini nasıl birim test edersiniz?


Yanıtlar:


61

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!


1
Bu, bir HttpDispatchTask kullanan görevler dışında çalışır - docs.celeryproject.org/en/latest/userguide/remote-tasks.html, burada celery.conf.CELERY_ALWAYS_EAGER = True ayarlamam gerekir, ancak celery.conf ayarlandığında bile.CELERY_IMPORTS = ('celery.task.http') test NotRegistered ile başarısız oluyor: celery.task.http.HttpDispatchTask
davidmytton

Garip, bazı içe aktarma sorunları yaşamadığına emin misin? Bu test işe yarıyor (yanıtı taklit ettiğimi, böylece kerevizin beklediğini döndürdüğünü unutmayın) Ayrıca, CELERY_IMPORTS'da tanımlanan modüller, çalışanların başlatılması sırasında içe aktarılacaktır, bundan kaçınmak için aramanızı öneririm celery.loader.import_default_modules().
FlaPer87

Ayrıca buraya bir göz atmanızı öneririm . Http talebiyle dalga geçer. Dunno yardımcı olup olmadığını bilmiyor, sanırım çalışır durumda olan bir hizmeti test etmek istiyorsun, değil mi?
FlaPer87

52

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.


1
Bunun modası geçmiş olduğunu düşünüyorum - anlıyorum ImportError: No module named celeryconfig.
Daenyth

7
Yukarıda, modülün celeryconfig.pybirinin paketinde bulunduğunu varsaydığına inanıyorum . Docs.celeryproject.org/en/latest/getting-started/… bakın .
Kamil Sindi

1
Eski olduğunu biliyorum, ancak addbir TestCasesınıf içinde OP'nin sorusundan nasıl görev başlatılacağına dair tam bir örnek verebilir misiniz ?
Kruupös

@ MaxChrétien üzgünüm, artık kereviz kullanmadığım için tam bir örnek veremiyorum. Yeterli itibar puanınız varsa sorumu düzenleyebilirsiniz. Yeterli bilginiz yoksa, lütfen bu yanıta neyi kopyalayıp yapıştırmam gerektiğini bana bildirin.
guettli

1
@ miken32 teşekkürler. En son yanıt, yardımcı olmak istediğim sorunu bir şekilde ele aldığından, 4.0 için resmi belgelerin CELERY_TASK_ALWAYS_EAGERbirim testi için kullanılmasını caydırdığı yönünde bir yorum bıraktım .
krassowski

33

Tam olarak neyi test etmek istediğinize bağlı.

  • Görev kodunu doğrudan test edin. "Task.delay (...)" çağırmayın sadece birim testlerinizden "görev (...)" çağırın.
  • CELERY_ALWAYS_EAGER kullanın . Bu, görevlerinizin "task.delay (...)" dediğiniz noktada hemen çağrılmasına neden olur, böylece tüm yolu test edebilirsiniz (ancak hiçbir zaman uyumsuz davranışı değil).

24

ünite testi

import unittest

from myproject.myapp import celeryapp

class TestMyCeleryWorker(unittest.TestCase):

  def setUp(self):
      celeryapp.conf.update(CELERY_ALWAYS_EAGER=True)

py.test fikstürleri

# 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):
    ...

Ek: send_task'e saygı duyulmasını istekli hale getirin

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

23

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.

https://docs.celeryproject.org/en/latest/history/whatsnew-4.0.html?highlight=what%20is%20new#lowercase-setting-names


11
Resmi belgelere göre , "task_always_eager" (eski "CELERY_ALWAYS_EAGER") kullanımı birim testi için uygun değildir. Bunun yerine Kereviz uygulamanızı test etmek için başka harika yollar önerirler.
krassowski

4
Ünite testlerinizde istekli görevler istememenizin nedeninin, o zaman test etmemeniz olduğunu ekleyeceğim, örneğin, kodu üretimde kullandığınızda gerçekleşecek olan parametrelerin serileştirilmesi.
damd

15

İtibariyle Kereviz 3.0 , ayarlamak için bir yol CELERY_ALWAYS_EAGERiç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())

7

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_configfikstü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.


Sevgili olumsuz oy veren, bunun neden kötü bir cevap olduğunu paylaşmak ister misiniz? İçtenlikle teşekkürler.
alanjds

2
Benim için işe yaramadı, test odası kilitleniyor. Biraz daha içerik sağlayabilir misin? (Henüz oy vermedim;)).
dualite_

Benim durumumda celey_config fikstürünü bellek aracısını ve önbellek + bellek arka
ucunu

5

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

2

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_taskdekoratö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_idihtiyacı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 .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.