Unit Testing mit Django-Sellerie?


82

Ich versuche, eine Testmethode für unser Django-Sellerie- Projekt zu entwickeln. Ich habe die Notizen in der Dokumentation gelesen , aber es gab mir keine gute Vorstellung davon, was ich tatsächlich tun sollte. Ich mache mir keine Sorgen um das Testen der Aufgaben in den eigentlichen Daemons, sondern nur um die Funktionalität meines Codes. Hauptsächlich frage ich mich:

  1. Wie können wir task.delay()während des Tests umgehen (ich habe versucht einzustellen, CELERY_ALWAYS_EAGER = Trueaber es machte keinen Unterschied)?
  2. Wie verwenden wir die empfohlenen Testeinstellungen (wenn dies der beste Weg ist), ohne unsere settings.py tatsächlich zu ändern?
  3. Können wir noch manage.py testeinen benutzerdefinierten Läufer verwenden oder müssen wir ihn verwenden?

Insgesamt wären Hinweise oder Tipps zum Testen mit Sellerie sehr hilfreich.


1
Was meinst du CELERY_ALWAYS_EAGERmacht keinen Unterschied?
fragt

Ich erhalte immer noch Fehler, weil ich rabbitmq nicht kontaktieren kann.
Jason Webb

Hast du den Traceback? Ich denke, etwas anderes als .delayder Versuch, eine Verbindung herzustellen.
fragt

11
Die Einstellung BROKER_BACKEND=memorykönnte in diesem Fall hilfreich sein.
fragt

Fragen Sie, ob Sie Recht hatten. BROKER_BACKEND=memorybehoben. Wenn Sie das als Antwort geben, werde ich es als richtig markieren.
Jason Webb

Antworten:


43

Versuchen Sie die Einstellung:

BROKER_BACKEND = 'memory'

(Danke an askols Kommentar.)


7
Ich glaube, dass dies nicht mehr notwendig ist, wenn CELERY_ALWAYS_EAGER gesetzt ist.
mlissner

2
Haben Sie eine Lösung für Sellerie 4 gefunden?
David Schumann

71

Ich verwende den Dekorator override_settings gerne für Tests, für deren Abschluss Sellerieergebnisse erforderlich sind.

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

Wenn Sie dies auf alle Tests anwenden möchten, können Sie den Sellerie-Testläufer verwenden, wie unter http://docs.celeryproject.org/en/2.5/django/unit-testing.html beschrieben, der im Grunde dieselben Einstellungen außer ( BROKER_BACKEND = 'memory') festlegt .

In den Einstellungen:

TEST_RUNNER = 'djcelery.contrib.test_runner.CeleryTestSuiteRunner'

Schauen Sie sich die Quelle für CeleryTestSuiteRunner an und es ist ziemlich klar, was passiert.


1
Dies funktionierte nicht mit Sellerie 4, auch wenn das Feld von hier umbenannt wurde
Shadi

Arbeiten an Sellerie 3.1. Ich habe gerade meine Sellerie-Testfälle von einer Elternklasse mit diesem Dekorateur geerbt. Auf diese Weise wird es nur an einer Stelle benötigt und es besteht keine Notwendigkeit, es einzuziehen djcelery.
kontextify

1
Dies funktioniert hervorragend bei Sellerie 4.4. und Django 2.2. Bester Ansatz für Unit-Tests, den ich bisher gefunden habe.
Erik Kalkoken

18

Hier ist ein Auszug aus meiner Testbasisklasse, in dem die apply_asyncMethode herausgearbeitet und die Aufrufe aufgezeichnet werden (einschließlich Task.delay). Es ist ein wenig eklig, aber es hat meine Anforderungen in den letzten Monaten erfüllt, in denen ich es verwendet habe.

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)

Hier ist ein Beispiel dafür, wie Sie es in Ihren Testfällen verwenden würden:

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

Da ich dies immer noch in den Suchergebnissen sehe, überschreiben die Einstellungen mit

TEST_RUNNER = 'djcelery.contrib.test_runner.CeleryTestSuiteRunner'

arbeitete für mich gemäß Celery Docs


0

Für alle, die 2019 hierher kommen: Lesen Sie diesen Artikel , in dem verschiedene Strategien behandelt werden, einschließlich des synchronen Aufrufs von Aufgaben.


0

Das habe ich getan

In myapp.tasks.py habe ich:

from celery import shared_task

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

In myapp.test_tasks.py habe ich:

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.