2 min read

Unit Testing Celery Tasks

Testing distributed tasks can be tricky because they usually run asynchronously in a separate worker process. However, for unit tests, you typically want to run tasks synchronously (locally) to verify their logic without needing a running message broker or worker.

1. Using task_always_eager

The most common way to test Celery tasks is to set the task_always_eager configuration setting to True. This tells Celery to execute tasks locally and synchronously (blocking) instead of sending them to the queue.

Configuration

You can set this in your test setup or override the settings temporarily.

from celery import Celery
import unittest

app = Celery('tests')

class TestCeleryTasks(unittest.TestCase):
    def setUp(self):
        # Enable eager mode
        app.conf.update(task_always_eager=True)

    def test_add_task(self):
        from tasks import add

        # This will run locally and return the result immediately
        result = add.delay(2, 2)

        self.assertEqual(result.get(), 4)
        self.assertTrue(result.successful())

task_eager_propagates

By default, if a task raises an exception in eager mode, it will be stored in the result backend but not raised in the test. To make tests fail immediately on task errors, set task_eager_propagates=True.

app.conf.update(task_always_eager=True, task_eager_propagates=True)

2. Using .apply() (Direct Execution)

If you don't want to change the global configuration, you can execute a task directly using the .apply() method instead of .delay() or .apply_async().

from tasks import add

def test_add_direct():
    # apply() executes the task inline
    result = add.apply(args=(2, 2))
    assert result.get() == 4

3. Mocking Tasks

Sometimes you don't want to execute the task at all (e.g., if it sends an email or charges a credit card). You just want to ensure that the task would have been called.

from unittest.mock import patch
from my_app import register_user

@patch('my_app.send_welcome_email.delay')
def test_register_user(mock_send_email):
    register_user('alice@example.com')

    # Verify the task was queued
    mock_send_email.assert_called_with('alice@example.com')

programming/python/celery