Python: 模拟在 celery 任务中不起作用
Python: Mock doesn't work inside celery task
我想使用 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'
是否可以执行这样的集成测试,或者解决此问题的唯一方法是将测试分成两个?
直接从使用它们的代码测试异步任务的行为可能很棘手。一个原因是测试 可能 甚至在任务实际运行之前就执行断言,这可能会给您误报。在这种情况下,我所做的是将测试分为两步:
- 模拟任务并测试它在必须被调用时被调用并使用预期的参数。
- 将任务作为独立函数进行测试,并将其作为普通函数执行,即不需要 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'
通过这种方式,您可以测试您的实现代码是否在您的任务方面表现正确,以及任务在按预期调用后表现是否正确。
希望有用! :)
我发现了一个问题,它很傻。 Described here and Here:
The basic principle is that you patch where an object is looked up, which is not necessarily the same place as where it is defined.
我需要更改:
@mock.patch('django.core.mail.mail_managers')
和
@mock.patch('path.to.tasks.mail_managers')
我想使用 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'
是否可以执行这样的集成测试,或者解决此问题的唯一方法是将测试分成两个?
直接从使用它们的代码测试异步任务的行为可能很棘手。一个原因是测试 可能 甚至在任务实际运行之前就执行断言,这可能会给您误报。在这种情况下,我所做的是将测试分为两步:
- 模拟任务并测试它在必须被调用时被调用并使用预期的参数。
- 将任务作为独立函数进行测试,并将其作为普通函数执行,即不需要 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'
通过这种方式,您可以测试您的实现代码是否在您的任务方面表现正确,以及任务在按预期调用后表现是否正确。
希望有用! :)
我发现了一个问题,它很傻。 Described here and Here:
The basic principle is that you patch where an object is looked up, which is not necessarily the same place as where it is defined.
我需要更改:
@mock.patch('django.core.mail.mail_managers')
和
@mock.patch('path.to.tasks.mail_managers')