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ブローカーの場合と同じです。結果を待つテストをブロックします。テストで機能するようにこのブローカーをどのように構成する必要があるのでしょうか?
設定で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):
...
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アプリインスタンスと同じアプリを使用していることを確認する必要があります。
ワーカーを起動すると、多くのものが印刷され、ログ設定が変更されることに注意してください。
これは、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()
これによってキュー名がテストキューに変更されることはないことに注意してください。したがって、アプリも実行している場合は、それも実行する必要があります。