使用mock在Django单元测试中修补芹菜任务

时间:2013-08-13 14:40:59

标签: python django unit-testing mocking celery

我正在尝试使用python模拟库来修补在我的django应用程序中保存模型时运行的Celery任务,以查看它是否被正确调用。

基本上,任务是在myapp.tasks内定义的,并且在我的models.py文件的顶部导入,如下所示:

from .tasks import mytask

...然后使用save()在模型内的mytask.delay(foo, bar)上运行。到目前为止一切都那么好 - 当我真正运行Celeryd等时,工作得很好。

我想构建一个模拟任务的单元测试,只是检查它是否使用正确的参数调用,并且实际上并没有尝试运行Celery任务。

所以在测试文件中,我在标准TestCase中有这样的东西:

from mock import patch # at the top of the file

# ...then later
def test_celery_task(self):
    with patch('myapp.models.mytask.delay') as mock_task:
        # ...create an instance of the model and save it etc
        self.assertTrue(mock_task.called)

...但它永远不会被调用/总是错误的。我尝试了各种版本(修补myapp.models.mytask代替,并检查是否调用了mock_task.delay。我从模拟文档中收集了导入路径至关重要,谷歌搜索告诉我它应该是在测试模块中看到的路径(如果我理解正确的话,它将是myapp.models.mytask.delay而不是myapp.tasks.mytask.delay

我在哪里错了?在修补Celery任务时是否存在一些特定的困难?我可以修补celery.task(用作mytask的装饰器)吗?

2 个答案:

答案 0 :(得分:34)

您遇到的问题与这是Celery任务无关。你碰巧正在修补错误的东西。 ;)

具体来说,你需要找出哪个视图或其他文件正在导入“mytask”并在那里修补它,所以相关的行看起来像这样:

with patch('myapp.myview.mytask.delay') as mock_task:

这里有更多的味道:

http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch

答案 1 :(得分:23)

@task装饰器用Task对象替换该函数(参见documentation)。如果您模拟任务本身,您将用Task替换(有些神奇的)MagicMock对象,它根本不会安排任务。而是模仿Task对象的run()方法,如下所示:

@override_settings(CELERY_ALWAYS_EAGER=True)
@patch('monitor.tasks.monitor_user.run')
def test_monitor_all(self, monitor_user):
    """
    Test monitor.all task
    """

    user = ApiUserFactory()
    tasks.monitor_all.delay()
    monitor_user.assert_called_once_with(user.key)