问题
I have a task foobar
:
@app.task(bind=True)
def foobar(self, owner, a, b):
if already_working(owner): # check if a foobar task is already running for owner.
register_myself(self.request.id, owner) # add myself in the DB.
return a + b
How can I mock the self.request.id
attribute? I am already patching everything and calling directly the task rather than using .delay/.apply_async
, but the value of self.request.id
seems to be None
(as I am doing real interactions with DB, it is making the test fail, etc…).
For the reference, I'm using Django as a framework, but I think that this problem is just the same, no matter the environment you're using.
回答1:
Disclaimer: Well, I do not think it was documented somewhere and this answer might be implementation-dependent.
Celery wraps his tasks into celery.Task
instances, I do not know if it swaps the celery.Task.run
method by the user task function or whatever.
But, when you call a task directly, you call __call__
and it'll push a context which will contain the task ID, etc…
So the idea is to bypass __call__
and Celery usual workings, first:
- we push a controlled task ID:
foobar.push_request(id=1)
for example. - then, we call the run method:
foobar.run(*args, **kwargs)
.
Example:
@app.task(bind=True)
def foobar(self, name):
print(name)
return foobar.utils.polling(self.request.id)
@patch('foobar.utils.polling')
def test_foobar(mock_polling):
foobar.push_request(id=1)
mock_polling.return_value = "done"
assert foobar.run("test") == "done"
mock_polling.assert_called_once_with(1)
回答2:
You can call the task synchronously using
task = foobar.s(<args>).apply()
This will assign a unique task ID, so the value will not be None and your code will run. Then you can check the results as part of your test.
来源:https://stackoverflow.com/questions/47000371/mocking-celery-self-request-attribute-for-bound-tasks-when-called-directly