Unit testing an AsyncResult in celery

2019-03-15 15:39发布

问题:

I am trying to test some celery functionality in Django's unit testing framework, but whenever I try to check an AsyncResult the tests act like it was never started.

I know this code works in a real environment with RabbitMQ, so I was just wondering why it didn't work when using the testing framework.

Here is an example:

@override_settings(CELERY_EAGER_PROPAGATES_EXCEPTIONS = True,
                   CELERY_ALWAYS_EAGER = True,
                   BROKER_BACKEND = 'memory',)
def test_celery_do_work(self):
    result = myapp.tasks.celery_do_work.AsyncResult('blat')
    applied_task = myapp.tasks.celery_do_work.apply_async((), task_id='blat')
    applied_task.wait()
    # THIS SUCCEEDS
    self.assertTrue(applied_task.successful())
    # THIS FAILS
    self.assertTrue(result.successful())

Does using the ALWAYS_EAGER option disable the AsyncResult functionality since it's executing immediately? If so, is there any way to able to unit test AsyncResult status checking? If I try to take out the ALWAYS_EAGER option the tests never run, so I am at a loss.

Thanks!

回答1:

When CELERY_ALWAYS_EAGER is True, the call to apply_async() is actually replaced with apply(). The result returned is an EagerResult, which already contains the result of your task.

So, yes, setting ALWAYS_EAGER = True disables the entire AsyncResult functionnality. The entire async process is bypassed, and no task is actually sent to the broker, which is why you cannot retrieve the result through an AsyncResult.

Use CELERY_ALWAYS_EAGER = True when you are testing code path which just need a Celery result, and work the same way with an EagerResult or an AsyncResult.

If needed, there is a way to run tests with AsyncResult too, with CELERY_ALWAYS_EAGER = False, but for this, you'll need to start a worker before calling the task in your test case. The worker will then be able to execute your task, and AsyncResult will work just fine. You can take a look at django-celery-testworker which seems to do just that, although I've not tested it.