我正在使用Django 1.10和Celery 4.1
我有shared_task
向用户发送电子邮件。
# myapp/tasks.py
@shared_task
def notify_user(user_id):
# TODO: send email and do other stuff here
user = get_object_or_404(User, pk=user_id)
send_mail(
'Subject',
'Body',
'from@example.com',
[user.email],
)
我有另一个文件,其中包含一个调用将该任务放入队列的函数。
# myapp/utils.py
# ...
def update_queue(self):
# increment no_of_used_referrals by 1
if no_of_used_referrals == 5:
notify_user.apply_async((self.user_id,))
else:
notify_user.apply_async((self.user_id,), eta=new_eta)
现在我正在尝试测试调用update_queue()
(所有必需的检查都通过)是否在执行时向用户发送电子邮件。
我尝试执行以下操作:
# myapp/tests.py
def update_queue_should_call_notify_user_immediately_after_five_referrals_were_used(self):
with unittest.mock.patch('myapp.tasks.notify_user.apply_async') as notify_user_mock:
# ...
for _ in range(5):
entry.update_queue()
self.assertTrue(notify_user_mock.called)
notify_user_mock.assert_called_with((user_id,))
# TODO: check if email was sent
# I tried using :
# self.assertEqual(len(mail.outbox), 1)
# but it fails with error saying 0 != 1
def test_notify_user_should_send_an_email(self):
notify_user.apply_async((user_id,))
# I tried using:
# self.assertEqual(len(mail.outbox), 1)
# but it fails with error saying 0 != 1
我在项目设置中设置了EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'
。
有人可以告诉我我在做什么以及如何正确测试这个案子有什么问题吗?
修改 我更新了我的代码,我排除了嘲弄 - 正如@DanielRoseman所建议的那样。
EDIT2 请参阅上面的更新文件。
我正在模拟推荐系统。一旦使用了与特定用户相关联的5个推荐链接,用户就可以获得他们的个人资料的一些不错的功能。否则他们必须等待特定时间,我在eta
使用apply_async
参数设置。
每次打电话给update_queue
时,我都会检查参考号码是否等于5(请参阅上面的更新代码)。
notify_user
,而不传递eta
参数值。 notify_user
任务,使用新的notify_user
参数值创建新的eta
任务。为了测试我是否在for循环中模拟该行为,我想测试是否在5次迭代(相当于5次使用的推荐链接)之后向用户发送了一封电子邮件(出于测试目的,我使用了 - 内存后端电子邮件)。
答案 0 :(得分:1)
我把它放在这里是给面临同样问题的人的。
我已经解决了
TEST_RUNNER = 'djcelery.contrib.test_runner.CeleryTestSuiteRunner'
https://stackoverflow.com/a/46531472/7396169
我认为该解决方案适合单元测试。
答案 1 :(得分:0)
from django.core.mail import EmailMessage
from django.template.loader import render_to_string
from django.contrib.auth import get_user_model
from accounts.models import Token
from celery import shared_task
@shared_task(bind=True)
def send_login_email_task(self, email):
try:
uid = str(uuid.uuid4())
Token.objects.create(email=email, uid=uid)
current_site = 'localhost:8000'
mail_subject = 'Activate your account.'
message = render_to_string('accounts/login_activation_email.html', {
'domain': current_site,
'uid': uid
})
print('called')
email = EmailMessage(mail_subject, message, to=[email])
email.send()
except Token.DoesNotExist:
logging.warning(
"Tried to send activation email to non-existing user '%s'", email)
except smtplib.SMTPException as exc:
raise self.retry(exc=exc)
from django.test import TestCase
from unittest.mock import patch
from django.contrib.auth import get_user_model
from celery.exceptions import Retry
from proj.celery import App
import smtplib
import uuid
import accounts.tasks
from accounts.models import Token
@patch('accounts.tasks.EmailMessage')
def test_send_login_email_task(self, mock_email_message):
# call task
token = Token.objects.get(email=self.email, uid=self.uid)
print(token.email)
accounts.tasks.send_login_email_task.apply_async((token.email,))
self.assertEqual(mock_email_message.called, True)
# patch EmailMessage
print(mock_email_message.call_args)
args, kwargs = mock_email_message.call_args
subject = args[0]
self.assertEqual(subject, 'Activate your account.')
self.assertEqual(kwargs, {'to': ['ama@example.com']})