web-dev-qa-db-ja.com

セロリユニットテスト用のインメモリブローカー

REST APIがDjangoで記述されており、投稿時にセロリタスクをキューに入れるエンドポイントがあります。応答には、タスクが次のことをテストするために使用するタスクIDが含まれています作成して結果を取得するので、次のようなことをしたいと思います。

def test_async_job():
    response = self.client.post("/api/jobs/", some_test_data, format="json")
    task_id = response.data['task_id']
    result = my_task.AsyncResult(task_id).get()
    self.assertEquals(result, ...)

私は明らかに、ユニットテストを実行するためにセロリワーカーを実行する必要はありません。どういうわけかそれをモックすることを期待しています。 CELERY_ALWAYS_EAGER を使用できません。これは、ブローカーを完全にバイパスしているようで、AsyncResultを使用してそのIDでタスクを取得できないためです( ここ )。

セロリと kombu docs を調べてみると、ユニットテスト用のメモリ内トランスポートがあり、それが私が探していることを実行することがわかりました。 BROKER_URL設定をオーバーライドして、テストで使用してみました。

@override_settings(BROKER_URL='memory://')
def test_async_job():

ただし、動作はampqブローカーの場合と同じです。結果を待つテストをブロックします。テストで機能するようにこのブローカーをどのように構成する必要があるのでしょうか?

28
Facundo Olano

設定でbroker_backendを指定できます。

if 'test' in sys.argv[1:]:
    BROKER_BACKEND = 'memory'
    CELERY_TASK_ALWAYS_EAGER = True
    CELERY_TASK_EAGER_PROPAGATES = True

または、テストで直接デコレータを使用して設定を上書きできます

import unittest
from Django.test.utils import override_settings


class MyTestCase(unittest.TestCase):

    @override_settings(CELERY_TASK_EAGER_PROPAGATES=True,
                       CELERY_TASK_ALWAYS_EAGER=True,
                       BROKER_BACKEND='memory')
    def test_mytask(self):
        ...
13

Kombuインメモリブローカーを使用して単体テストを実行できますが、そのためには、Djangoサーバーと同じCeleryアプリオブジェクトを使用して、Celeryワーカーを起動する必要があります。

インメモリブローカーを使用するには、BROKER_URLをmemory://localhost/に設定します

次に、小さなセロリワーカーをスピンアップするには、次のようにします。

app = <Django Celery App>

# Set the worker up to run in-place instead of using a pool
app.conf.CELERYD_CONCURRENCY = 1
app.conf.CELERYD_POOL = 'solo'

# Code to start the worker
def run_worker():
    app.worker_main()

# Create a thread and run the worker in it
import threading
t = threading.Thread(target=run_worker)
t.setDaemon(True)
t.start()

Django celeryアプリインスタンスと同じアプリを使用していることを確認する必要があります。

ワーカーを起動すると、多くのものが印刷され、ログ設定が変更されることに注意してください。

9
Gal Hochberg

これは、Celery4.xで動作するDjango TransactionTestCaseのより完全な機能を備えた例です。

import threading

from Django.test import TransactionTestCase
from Django.db import connections

from myproj.celery import app  # your Celery app


class CeleryTestCase(TransactionTestCase):
    """Test case with Celery support."""

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        app.control.purge()
        cls._worker = app.Worker(app=app, pool='solo', concurrency=1)
        connections.close_all()
        cls._thread = threading.Thread(target=cls._worker.start)
        cls._thread.daemon = True
        cls._thread.start()

    @classmethod
    def tearDownClass(cls):
        cls._worker.stop()
        super().tearDownClass()

これによってキュー名がテストキューに変更されることはないことに注意してください。したがって、アプリも実行している場合は、それも実行する必要があります。

7