在 Django 单元测试中使用 mock 修补 celery 任务
Posted
技术标签:
【中文标题】在 Django 单元测试中使用 mock 修补 celery 任务【英文标题】:Using mock to patch a celery task in Django unit tests 【发布时间】:2013-08-15 05:57:56 【问题描述】:我正在尝试使用 python 模拟库来修补在我的 django 应用程序中保存模型时运行的 Celery 任务,以查看它是否被正确调用。
基本上,任务是在 myapp.tasks
中定义的,并像这样在我的 models.py 文件的顶部导入:
from .tasks import mytask
...然后使用mytask.delay(foo, bar)
在模型内部的save()
上运行。到目前为止一切顺利 - 当我实际运行 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
的装饰器)吗?
【问题讨论】:
您是否尝试过设置“CELERY_ALWAYS_EAGER = True”而不是模拟它? 【参考方案1】:@task
装饰器将函数替换为Task
对象(请参阅documentation)。如果您模拟任务本身,您将用MagicMock
替换(有点神奇)Task
对象,它根本不会安排任务。而是模拟 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)
【讨论】:
您在两个问题上逐字逐句地发布这个答案是有原因的吗? @Micheled'Amico:我不同意,如果你模拟这个解释的任务本身,答案就谈到了它没有运行的具体问题。模拟delay()
方法,虽然可能在特定情况下工作实际上是错误的答案,因为您以后可能会更改代码以使用 apply_async()
或其他方法,突然您的测试会因为错误的原因而中断。
+1 @DanielleMadeley。我最初修补了延迟并且测试运行良好,直到我们需要将它们链接为子任务并且一切都匆匆忙忙!
在较新的版本中,我们需要使用CELERY_TASK_ALWAYS_EAGER
我认为这是一个更好的答案,因为它适用于链式任务以及使用apply_async
调用的任务(提供countdown
参数)。【参考方案2】:
您遇到的问题与这是 Celery 任务这一事实无关。你只是碰巧修补了错误的东西。 ;)
具体来说,您需要找出哪个视图或其他文件正在导入“mytask”并在那里对其进行修补,因此相关行如下所示:
with patch('myapp.myview.mytask.delay') as mock_task:
这里有更多的味道:
http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch
【讨论】:
干杯!我还没有尝试过(项目现在处于休眠状态)但很快就会尝试并将其标记为已回答。我似乎记得在你建议的主题上尝试了一堆变体,但很有可能我当时的血糖很低...... :-) 实际上,我正在按照您的建议执行此操作,如问题代码中所示...无法正常工作。哦,好吧。 问题是修补模型。这闻起来很不对劲,因为我怀疑您没有在模型中使用“延迟”,而是在其他地方使用 - 可能是视图,因此我的补丁代码(上图)略有不同。 好吧,实际上,我在模型上被覆盖的save()
方法中调用了mytask.delay()
。 (我正在使用该任务将一些数据从模型发送到外部系统)。这会对修补的工作方式产生影响吗?
返回并从头开始尝试一切:这一次效果很好。不知道我以前做错了什么,但我很高兴。 :-)以上是关于在 Django 单元测试中使用 mock 修补 celery 任务的主要内容,如果未能解决你的问题,请参考以下文章
使用 unittest.mock 在 python 中修补 SMTP 客户端
使用unittest.mock在python中修补SMTP客户端