直接调用绑定任务时,模拟Celery`self.request`属性

时间:2017-10-29 12:52:13

标签: django unit-testing celery

我有一个任务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

如何模拟self.request.id属性?我已修补所有内容并直接调用该任务而不是使用.delay/.apply_async,但self.request.id的值似乎是None(因为我正在与DB进行真正的交互,它正在制作测试失败等...)。

作为参考,我使用Django作为框架,但我认为无论您使用的环境如何,这个问题都是一样的。

3 个答案:

答案 0 :(得分:2)

免责声明:嗯,我不认为它是在某处记录的,而且这个答案可能与实现有关。

Celery将他的任务包装到celery.Task实例中,我不知道它是否通过用户任务函数或其他任何方式交换celery.Task.run方法。

但是,当你直接调用一个任务时,你会调用__call__并且它会推送一个包含任务ID等的上下文......

所以我的想法是绕过__call__和芹菜的常规工作,首先:

  • 我们推送受控任务ID:例如foobar.push_request(id=1)
  • 然后,我们调用run方法:foobar.run(*args, **kwargs)

示例:

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

答案 1 :(得分:1)

您可以使用来同步调用任务

task = foobar.s(<args>).apply()

这将分配一个唯一的任务ID,因此该值将不会为None,并且您的代码将运行。然后,您可以在测试中检查结果。

答案 2 :(得分:0)

可能有一种方法可以使用 patch 执行此操作,但我无法找到分配属性的方法。最直接的方法就是模拟自己。

tasks.py:

@app.task(name='my_task')
def my_task(self, *args, **kwargs):
    *__do some thing__*

test_tasks.py:

from mock import Mock

def test_my_task():
    self = Mock()
    self.request.id = 'ci_test'
    my_task(self)