【问题标题】:Python: Mock doesn't work inside celery task [duplicate]Python:模拟在芹菜任务中不起作用[重复]
【发布时间】:2015-08-12 10:35:57
【问题描述】:

我想使用 python mock 库来测试我的 Django 应用程序是否发送电子邮件。

测试代码:

# tests.py
from django.test import TestCase

class MyTestCase(TestCase):

    @mock.patch('django.core.mail.mail_managers')
    def test_canceled_wo_claiming(self, mocked_mail_managers):
        client = Client()
        client.get('/')
        print(mocked_mail_managers.called)
        mocked_mail_managers.assert_called_with('Hi, managers!', 'Message Body')

第一个示例 - 没有任务

# views.py
from django.views.generic import View
from django.core.mail import mail_managers

class MyView(View):

    def get(self, request):
        mail_managers('Hi, managers!', 'Message Body')
        return HttpResponse('Hello!')

第二个例子 - 任务

# views.py
from django.views.generic import View
from . import tasks

class MyView(View):
    def get(self, request):
        tasks.notify.apply_async()
        return HttpResponse('Hello!')


# tasks.py
from celery import shared_task
from django.core.mail import mail_managers

@shared_task
def notify():
    mail_managers('Hi, managers!', 'Message Body')

第一个示例工作正常,第二个示例失败,Not called 异常。

我的设置:

# Celery
BROKEN_URL = 'memory://'
BROKER_BACKEND = 'memory'

CELERY_ALWAYS_EAGER = True
CELERY_EAGER_PROPAGATES_EXCEPTIONS = True
TEST_RUNNER = 'djcelery.contrib.test_runner.CeleryTestSuiteRunner'

是否可以进行这样的集成测试,或者解决这个问题的唯一方法是将测试分成两部分?

【问题讨论】:

    标签: python django unit-testing mocking celery


    【解决方案1】:

    我发现了一个问题,而且非常愚蠢。 Described hereHere:

    基本原则是你在查找对象的地方打补丁,不一定和定义的地方是同一个地方。

    我需要改变:

    @mock.patch('django.core.mail.mail_managers')
    

    @mock.patch('path.to.tasks.mail_managers')
    

    【讨论】:

    • 还有一点——我用task_name.delay(args)调用任务,所以我必须修补task_caller_location.task_name.delay,而不仅仅是task_caller_location.task_name
    【解决方案2】:

    直接从使用异步任务的代码测试异步任务的行为可能很棘手。一个原因是测试可能甚至在任务实际运行之前就执行断言,这可能会给您带来误报。在这种情况下,我所做的是将测试分为两个步骤:

    1. 模拟任务并在必须调用它时使用预期的参数测试它是否被调用。
    2. 将任务作为独立函数进行测试,并将其作为普通函数执行,即不需要 celery 服务器。

    为了说明,这可能是这样的:

    # views.py
    from path.to.tasks import my_task
    
    
    def my_view(requtest):
        # do stuff
        my_task.delay('foo', 'bar')
        return HttpResponse('whatever')
    
    
    # test_my_task.py
    from views import my_view
    from path.to.tasks import my_task
    
    
    class MyTest(TestCase):
        @mock.patch('path.to.tasks.my_task')
        def test_my_task_is_called(self, mocked_task):
            client = Client()
            client.get('/')
            my_task.assert_called_with('foo', 'bar')
    
        def test_my_task_works(self):
            my_task('foo', 'bar')  # note I don't use .delay(...), .apply_async(...), etc
            assert 'my task did what I expected it to do'
    

    通过这种方式,您可以测试您的实现代码在您的任务方面的行为是否正确,并且该任务在按预期调用后是否正确。

    希望对你有用! :)

    【讨论】:

    • 杰拉德,感谢您的写作!无法弄清楚官方文档,但您的方法效果很好!
    猜你喜欢
    • 2016-04-24
    • 2015-12-16
    • 1970-01-01
    • 1970-01-01
    • 2019-10-05
    • 2016-05-18
    • 1970-01-01
    • 2015-01-20
    • 1970-01-01
    相关资源
    最近更新 更多