【发布时间】:2018-02-11 22:28:18
【问题描述】:
我正在使用 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 个推荐链接,用户就会在他们的个人资料中获得一些不错的功能。否则他们必须等待一个特定的时间,这是我在apply_async 上使用eta 参数设置的。
每次我调用update_queue 时,我都会检查引用的数量是否等于 5(请参阅上面的更新代码)。
- 如果是 - 我想立即调用
notify_user,而不传递eta参数值。 - 如果不是 - 我增加使用的推荐链接的数量,撤销旧的
notify_user任务,使用新的eta参数值创建新的notify_user任务。
为了测试我在 for 循环中模拟该行为,并且我想测试在 5 次迭代(等于 5 次使用的推荐链接)后是否向用户发送了一封电子邮件(出于测试目的,我在 -电子邮件的内存后端)。
【问题讨论】:
-
我能想到的最好的方法是,您可以在数据库中为相应的用户添加一个字段,以便向用户发送电子邮件或不发送电子邮件。然后你可以在你的 celery 任务中更新这个字段,所以如果发送电子邮件成功,那么这个字段就会更新。这很容易测试
-
你已经模拟了整个任务,所以它自然不会真正做任何事情。你不应该模拟被测代码。
-
@DanielRoseman 我已经更新了我的答案。但是还是不行。
-
@ArpitSolanki 感谢您的建议,但这听起来像是不必要的开销,仅用于测试目的。如果我为我的一个数据库模型实现该属性,我将永远不会在
tests.py文件之外的任何地方使用它 -
很难知道你在做什么。你应该分别测试这些东西;一旦调用
update_queue并模拟了任务以断言它被调用,并且一旦直接调用任务然后检查test email outbox。请注意,您不应该更改电子邮件后端。
标签: python django unit-testing celery