尽管mail.outbox是空的,AnyMail还没有在Django测试中发送电子邮件?

时间:2018-06-06 21:19:28

标签: python django django-anymail

我想写一个Django单元测试来测试发送电子邮件而不实际发送它(如https://docs.djangoproject.com/en/2.0/topics/testing/tools/#email-services中所述),但也可以配置为发送实际的电子邮件(以检查其格式等等。)。

现在,我只进行了'普通'测试:

from django.test import TestCase, override_settings
from django.core import mail
from lucy_web.test_factories import FamilyFactory, UserFactory
from lucy_web.views.app_invite import send_activation_email


# @override_settings(EMAIL_BACKEND="anymail.backends.mailgun.EmailBackend")
class SendActivationEmailTestCase(TestCase):
    def test_send_activation_email(self):
        user = UserFactory(email='kurt@startwithlucy.com')
        family = FamilyFactory(employee_user=user)
        send_activation_email(user=user)

        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(mail.outbox[0].recipients(), [user.email])
        self.assertIn(
            f"Your activation code is {family.activation_code}.",
            mail.outbox[0].body)

(该测试针对某些型号使用factory_boy测试夹具)。此测试通过,但如果我在override_settings装饰器中发表评论,则会收到错误:

(venv) Kurts-MacBook-Pro-2:lucy-web kurtpeek$ python manage.py test lucy_web.tests.test_app_invite
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
F
======================================================================
FAIL: test_send_activation_email (lucy_web.tests.test_app_invite.SendActivationEmailTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/kurtpeek/Documents/Dev/lucy2/lucy-web/lucy_web/tests/test_app_invite.py", line 15, in test_send_activation_email
    self.assertEqual(len(mail.outbox), 1)
AssertionError: 0 != 1

----------------------------------------------------------------------
Ran 1 test in 0.787s

FAILED (failures=1)
Destroying test database for alias 'default'...

由于mail.outbox是空的,我希望我会收到一封实际的电子邮件,但是我检查了我的收件箱但没有收到任何内容。

为了完整起见,这是我正在测试的功能:

from django.conf import settings
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string


def send_activation_email(user):
    context = {
        'activation_code': user.family.activation_code,
        'page_title': 'Lucy Invitation',
        'company': user.family.package.company,
        'num_sessions': user.family.session_count,
        'domain': settings.EMAIL_IMAGE_DOMAIN}

    message_text = render_to_string('email/activation_code.txt', context)
    message_html = render_to_string('email/activation_code.html', context)

    email = EmailMultiAlternatives(
        subject='LUCY Invitation',
        body=message_text,
        from_email=settings.DEFAULT_FROM_EMAIL,
        to=[user.email],
        reply_to=[settings.SUPPORT_EMAIL])
    email.attach_alternative(message_html, "text/html")
    email.send()

为了增加我的困惑,从AnyMail文档(http://anymail.readthedocs.io/en/stable/tips/test_backend/#testing-your-app),似乎AnyMail还会在测试中捕获outbox中的电子邮件。

有人可以指出这里可能出现的问题吗?为什么我既没有收到电子邮件,也没有看到mail.outbox中的任何内容?

1 个答案:

答案 0 :(得分:2)

如果您使用test EmailBackend

,Anymail只会捕获发件箱中的邮件
@override_settings(EMAIL_BACKEND="anymail.backends.test.EmailBackend")
#                                                  ^^^^
class SendActivationEmailTestCase(TestCase):
    ...

这并不适用于您的情况,因为您正在使用实时Mailgun EmailBackend。这确实会尝试发送消息(假设您也正确设置了MAILGUN_API_KEY - 并且由于email.send()没有引发错误,您可能会这样做。)

因此,您需要调查邮件似乎已发送的原因,但您似乎无法接收邮件。开始诊断任何电子邮件发送问题的最佳位置是Mailgun的日志:https://app.mailgun.com/app/logs/。 (请务必从左侧菜单中选择正确的发送域。)

  • 如果您看到测试消息的已接受和已发送事件,则问题出在接收端。 (检查垃圾邮件文件夹。)

  • 如果您看到已接受,然后是拒绝或失败事件,问题出在Mailgun:Anymail正在设法将您的电子邮件发布到Mailgun,但Mailgun拒绝或无法提供。检查日志事件以获取解释。 (一个与经过验证的发件人域名不匹配的from_email可能是原因。)

  • 如果你甚至没有在Mailgun的日志中看到一个Accepted事件,那么问题就是Python端:这个消息甚至没有发送到Mailgun。您可以通过查看Anymail在发送电子邮件时附加的anymail_status来了解更多信息:

    def send_activation_email(user):
        # ...
        email.send()
        print(email.anymail_status.status)  # should be {'queued'}
        print(email.anymail_status.esp_response.content)  # raw Mailgun API response
    

如果这些都没有显示问题的根源,请更新您的问题以包含原始Mailgun API响应以及来自Mailgun日志的任何相关事件。