简体   繁体   中英

AnyMail not sending an email in a Django test, despite that mail.outbox is empty?

I'd like to write a Django unit test which tests that an email is sent without actually sending it (as described in https://docs.djangoproject.com/en/2.0/topics/testing/tools/#email-services ), but can also be configured to send an actual email (to inspect its formatting, etc.).

Right now, I have just an 'ordinary' test:

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)

(The test makes use of factory_boy test fixtures for certain models). This test passes, but if I comment in the override_settings decorator, I get an error:

(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'...

Since the mail.outbox is empty, I would expect that I would receive an actual email, but I checked my inbox and didn't receive anything.

For the sake of completeness, here is the function I'm testing:

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()

To increase my confusion, from the AnyMail docs ( http://anymail.readthedocs.io/en/stable/tips/test_backend/#testing-your-app ), it would seem that AnyMail also captures emails in the outbox in a test.

Can someone point out might what be going wrong here? Why am I neither receiving an email, nor seeing anything in the mail.outbox ?

Anymail only captures messages in the outbox if you are using its test EmailBackend :

@override_settings(EMAIL_BACKEND="anymail.backends.test.EmailBackend")
#                                                  ^^^^
class SendActivationEmailTestCase(TestCase):
    ...

This doesn't apply in your case, because you're using the live Mailgun EmailBackend. That will indeed attempt to send the message (assuming you've also got MAILGUN_API_KEY set properly—and since the email.send() didn't raise an error, you probably do).

So you need to investigate why the mail appears to be sent, but you don't seem to be receiving it. The best place to start diagnosing any email sending issue is Mailgun's logs: https://app.mailgun.com/app/logs/ . (Be sure to select your correct sending domain from the menu on the left.)

  • If you see an Accepted and Delivered event for your test message, the problem is at the receiving end. (Check your spam folder.)

  • If you see Accepted, then Rejected or Fail events, the problem is at Mailgun: Anymail is managing to post your email to Mailgun, but Mailgun is refusing or unable to deliver it. Check the log events for an explanation. (A from_email that doesn't match a validated sender domain is a likely cause.)

  • If you don't even see an Accepted event in Mailgun's logs, then the problem is Python-side: the message isn't even making it to Mailgun. You may be able to find out more by checking the anymail_status that Anymail attaches to the email as it sends it:

     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 

If none of that reveals the source of the problem, please update your question to include the raw Mailgun API response and any relevant events from Mailgun's logs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM