django-celeryを使用したユニットテスト?


82

django-celeryプロジェクトのテスト方法を考え出そうとしています。ドキュメントのメモを読みましたが、実際に何をすべきかについてはよくわかりませんでした。実際のデーモンでタスクをテストすることについては心配していません。コードの機能だけを心配しています。主に私は疑問に思っています:

  1. task.delay()テスト中にバイパスするにはどうすればよいCELERY_ALWAYS_EAGER = Trueですか(設定を試みましたが、違いはありませんでした)?
  2. 実際にsettings.pyを変更せずに、推奨されるテスト設定をどのように使用しますか(それが最善の方法である場合)?
  3. 引き続き使用できますmanage.py testか、それともカスタムランナーを使用する必要がありますか?

全体として、セロリでテストするためのヒントやヒントは非常に役立ちます。


1
どういう意味CELERY_ALWAYS_EAGERですか?
asksol 2010年

それでも、rabbitmqに接続できないというエラーが発生します。
Jason Webb

トレースバックはありますか?.delay接続を確立しようとしている以外の何かがあると思います。
asksol 2010年

11
BROKER_BACKEND=memoryその場合、設定が役立つ可能性があります。
asksol 2010年

あなたが正しかったと聞いてください。 BROKER_BACKEND=memoryそれを修正しました。あなたがそれを答えとして置くならば、私はそれを正しいとマークします。
Jason Webb 2011年

回答:


43

設定してみてください:

BROKER_BACKEND = 'memory'

asksolのコメントに感謝します。)


8
CELERY_ALWAYS_EAGERが設定されていれば、これはもう必要ないと思います。
mlissner 2013年

3
セロリ4の解決策を見つけましたか?
David Schumann

72

セロリの結果を完了する必要があるテストでoverride_settingsデコレータを使用するのが好きです。

from django.test import TestCase
from django.test.utils import override_settings
from myapp.tasks import mytask

class AddTestCase(TestCase):

    @override_settings(CELERY_EAGER_PROPAGATES_EXCEPTIONS=True,
                       CELERY_ALWAYS_EAGER=True,
                       BROKER_BACKEND='memory')
    def test_mytask(self):
        result = mytask.delay()
        self.assertTrue(result.successful())

これをすべてのテストに適用する場合は、http://docs.celeryproject.org/en/2.5/django/unit-testing.htmlで説明されているように、セロリテストランナーを使用できます。BROKER_BACKEND = 'memory')。

設定で:

TEST_RUNNER = 'djcelery.contrib.test_runner.CeleryTestSuiteRunner'

CeleryTestSuiteRunnerのソースを見ると、何が起こっているのかがかなり明確です。


1
これはセロリ4では機能しませんでした。ここ
shadi 2018年

セロリ3.1で動作します。Celeryテストケースは、このデコレータを使用して親クラスから継承しています。そうすれば、それは1つの場所でのみ必要であり、引き込む必要はありませんdjcelery
kontextify

1
これはCelery4.4でうまく機能します。およびDjango2.2。私がこれまでに出くわしたユニットテストを実行するための最良のアプローチ。
Erik Kalkoken

18

apply_asyncこれは、メソッドをスタブアウトし、メソッドへの呼び出しを記録するテスト基本クラスからの抜粋です(これにはTask.delay。が含まれます)。少し粗雑ですが、過去数か月間使用してきた私のニーズにうまく適合しました。

from django.test import TestCase
from celery.task.base import Task
# For recent versions, Task has been moved to celery.task.app:
# from celery.app.task import Task
# See http://docs.celeryproject.org/en/latest/reference/celery.app.task.html

class CeleryTestCaseBase(TestCase):

    def setUp(self):
        super(CeleryTestCaseBase, self).setUp()
        self.applied_tasks = []

        self.task_apply_async_orig = Task.apply_async

        @classmethod
        def new_apply_async(task_class, args=None, kwargs=None, **options):
            self.handle_apply_async(task_class, args, kwargs, **options)

        # monkey patch the regular apply_sync with our method
        Task.apply_async = new_apply_async

    def tearDown(self):
        super(CeleryTestCaseBase, self).tearDown()

        # Reset the monkey patch to the original method
        Task.apply_async = self.task_apply_async_orig

    def handle_apply_async(self, task_class, args=None, kwargs=None, **options):
        self.applied_tasks.append((task_class, tuple(args), kwargs))

    def assert_task_sent(self, task_class, *args, **kwargs):
        was_sent = any(task_class == task[0] and args == task[1] and kwargs == task[2]
                       for task in self.applied_tasks)
        self.assertTrue(was_sent, 'Task not called w/class %s and args %s' % (task_class, args))

    def assert_task_not_sent(self, task_class):
        was_sent = any(task_class == task[0] for task in self.applied_tasks)
        self.assertFalse(was_sent, 'Task was not expected to be called, but was.  Applied tasks: %s' %                 self.applied_tasks)

これは、テストケースでどのように使用するかの「頭から離れた」例です。

mymodule.py

from my_tasks import SomeTask

def run_some_task(should_run):
    if should_run:
        SomeTask.delay(1, some_kwarg=2)

test_mymodule.py

class RunSomeTaskTest(CeleryTestCaseBase):
    def test_should_run(self):
        run_some_task(should_run=True)
        self.assert_task_sent(SomeTask, 1, some_kwarg=2)

    def test_should_not_run(self):
        run_some_task(should_run=False)
        self.assert_task_not_sent(SomeTask)

4

これはまだ検索結果に表示されるので、設定は次のように上書きされます

TEST_RUNNER = 'djcelery.contrib.test_runner.CeleryTestSuiteRunner'

セロリドキュメントに従って私のために働いた


1

これは私がしたことです

myapp.tasks.pyの中に私は持っています:

from celery import shared_task

@shared_task()
def add(a, b):
    return a + b

myapp.test_tasks.pyの中に私は持っています:

from django.test import TestCase, override_settings
from myapp.tasks import add


class TasksTestCase(TestCase):

    def setUp(self):
        ...

    @override_settings(CELERY_TASK_ALWAYS_EAGER=True,CELERY_TASK_EAGER_PROPOGATES=True)
    def test_create_sections(self):
        result= add.delay(1,2)
        assert result.successful() == True
        assert result.get() == 3

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.