How do I mock the bound context, or mock the celery task id?
Given a celery task like:
helpers.py:
from task import some_task
def some_helper():
some_task.delay(123)
in task.py:
@app.task(queue="abc", bind=True)
def some_task(self, some_number: int):
print(self.id) # how to mock this attribute access?
Simple test case:
from django.test.testcases import TestCase
from helpers import some_helper
class SomeTest(TestCase):
def test_some_helper(self):
some_helper()
I tried:
@patch("celery.app.base.Celery.task", return_value=lambda x: x)
I also tried:
class MockResult(dict):
def __getattr__(self, x):
return self[x]
...
def test_some_task(self):
cls = MockResult({"id": "asdf"})
bound_some_task = some_task.__get__(cls, MockResult)
bound_some_task(123)
Related:
Given a celery task that looks like:
@my_celery_app.task(bind=True)
def my_task(self):
if self.request.retries == 1:
my_method_to_invoke()
# Do work for first retry
elif self.request.retries == 2:
# Do work for second retry
# do work for main task
The test can set the self.request.retries
by mocking the base Task.request
class attribute within celery.
In the unit test the following can be done
@patch("path.to.task.my_method_to_invoke")
@patch("celery.app.task.Task.request")
def my_test_method(self, mock_task_request, mock_my_method_to_invoke):
# Set the value of retries directly
mock_task_request.retries = 1
# Call the task and assert the inside method was
# called
my_task()
mock_my_method_to_invoke.assert_called_once()
It may be possible to do the same with id
on Task. I was lead to this answer looking for how to mock the self
on a bound celery task.
Was able to get something working by using setattr
on the task method, not sure if there is a better/other ways to do this:
from django.test.testcases import TestCase
from helpers import some_helper
class SomeTest(TestCase):
def test_some_helper(self):
from task import some_task
setattr(some_task, 'id', 'hello-id')
some_helper()
In addition to this it is possible to mock the request.id or "task id" like so:
@patch("task.some_task.request.id", return_value="hello-id")
def test_some_helper(...): ....
What helped me was to create a mock class mimicking the task
class CeleryTaskHelper:
"""Mock task id"""
class Request:
@property
def id(self):
return ''.join(random.choices(string.ascii_uppercase + string.digits, k=20))
@property
def request(self):
return self.Request()
def revoke(self):
return
and then
@patch('apps.orders.tasks.activate_order.apply_async', return_value=CeleryTaskHelper())
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With