web-dev-qa-db-fra.com

Tests unitaires avec Django-céleri?

J'essaie de trouver une méthodologie de test pour notre projet Django-celery . J'ai lu les notes dans la documentation , mais cela ne m'a pas donné une bonne idée de ce que je dois réellement faire. Je ne suis pas inquiet de tester les tâches dans les démons réels, juste la fonctionnalité de mon code . Je me demande principalement:

  1. Comment pouvons-nous contourner task.delay() pendant le test (j'ai essayé de définir CELERY_ALWAYS_EAGER = True mais cela n'a fait aucune différence)?
  2. Comment utilisons-nous les paramètres de test recommandés (si c'est le meilleur moyen) sans réellement changer notre settings.py?
  3. Pouvons-nous encore utiliser manage.py test ou devons-nous utiliser un coureur personnalisé?

Dans l'ensemble, des conseils ou des astuces pour tester avec le céleri seraient très utiles.

80
Jason Webb

Essayez de définir:

BROKER_BACKEND = 'memory'

(Merci à askol commentaire de.)

42
a paid nerd

J'aime utiliser le décorateur override_settings sur les tests qui nécessitent des résultats de céleri pour terminer.

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())

Si vous souhaitez appliquer cela à tous les tests, vous pouvez utiliser le lanceur de test de céleri comme décrit sur http://docs.celryproject.org/en/2.5/Django/unit-testing.html qui définit essentiellement ces mêmes paramètres sauf (BROKER_BACKEND = 'memory').

Dans les paramètres:

TEST_RUNNER = 'djcelery.contrib.test_runner.CeleryTestSuiteRunner'

Regardez la source de CeleryTestSuiteRunner et c'est assez clair ce qui se passe.

67
joshua

Voici un extrait de ma classe de base de test qui stubs out le apply_async méthode et enregistre les appels à celle-ci (qui inclut Task.delay.) C'est un peu dégoûtant, mais il a réussi à répondre à mes besoins au cours des derniers mois, je l'ai utilisé.

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)

Voici un exemple "sur le dessus de la tête" de la façon dont vous l'utiliseriez dans vos cas de test:

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)
16
Sam Dolan

puisque je vois toujours cela apparaître dans les résultats de recherche, les paramètres remplacent

TEST_RUNNER = 'djcelery.contrib.test_runner.CeleryTestSuiteRunner'

travaillé pour moi selon Celery Docs

4
dorkforce

C'est ce que j'ai fait

Dans myapp.tasks.py, j'ai:

from celery import shared_task

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

Dans myapp.test_tasks.py, j'ai:

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
0

Pour tous ceux qui arrivent ici en 2019: consultez cet article couvrant différentes stratégies, y compris l'appel de tâches de manière synchrone.

0
Sibirsky