Testing email sending in Django

53,174

Solution 1

You can use a file backend for sending emails which is a very handy solution for development and testing; emails are not sent but stored in a folder you can specify!

Solution 2

Django test framework has some built in helpers to aid you with testing e-mail service.

Example from docs (short version):

from django.core import mail
from django.test import TestCase

class EmailTest(TestCase):
    def test_send_email(self):
        mail.send_mail('Subject here', 'Here is the message.',
            '[email protected]', ['[email protected]'],
            fail_silently=False)
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(mail.outbox[0].subject, 'Subject here')

Solution 3

If you are into unit-testing the best solution is to use the In-memory backend provided by django.

EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'

Take the case of use it as a py.test fixture

@pytest.fixture(autouse=True)
def email_backend_setup(self, settings):
    settings.EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'  

In each test, the mail.outbox is reset with the server, so there are no side effects between tests.

from django.core import mail

def test_send(self):
    mail.send_mail('subject', 'body.', '[email protected]', ['[email protected]'])
    assert len(mail.outbox) == 1

def test_send_again(self):
    mail.send_mail('subject', 'body.', '[email protected]', ['[email protected]'])
    assert len(mail.outbox) == 1

Solution 4

Use MailHog

Inspired by MailCatcher, easier to install.

Built with Go - MailHog runs without installation on multiple platforms.


Also, it has a component called Jim, the MailHog Chaos Monkey, which enables you to test sending emails with various problems happening:

What can Jim do?

  • Reject connections
  • Rate limit connections
  • Reject authentication
  • Reject senders
  • Reject recipients

Read more about it here.


(Unlike original mailcatcher, which failed on me when sending emails with emoji, encoded in UTF-8 and it WASN'T really fixed in the current release, MailHog just works.)

Solution 5

For any project that doesn't require sending attachments, I use django-mailer, which has the benefit of all outbound emails ending up in a queue until I trigger their sending, and even after they've been sent, they are then logged - all of which is visible in the Admin, making it easy to quickly check what you emailing code is trying to fire off into the intertubes.

Share:
53,174

Related videos on Youtube

RadiantHex
Author by

RadiantHex

hello! :)

Updated on June 04, 2020

Comments

  • RadiantHex
    RadiantHex almost 4 years

    I need to test that my Django application sends e-mails with correct content. I don't want to rely on external systems (like an ad-hoc gmail account), since I'm not testing the actual e-mail service...

    I would like to, maybe, store the emails locally, within a folder as they are sent. Any tip on how to achieve it?

    • nemesisdesign
      nemesisdesign about 6 years
      Moderators: please lock this question. Lots of spam is being added in the answers, proposing solutions that are ridiculously complex just to promote external services.
    • djvg
      djvg over 2 years
  • Steve Jalim
    Steve Jalim over 13 years
    Further to that, the Message objects created by django-mailer mean you can prod them (and inspect their contents) in unit tests too (I know that there's outbound mailbox support in the test suite for a dummy mailbox, but using django-mailer doesn't send mail unless the management command sends it, which means you can't use that mailbox object)
  • santiagobasulto
    santiagobasulto about 11 years
    +1 Good answer. But I it's not useful for complex cases, when send_mail can't be used.
  • Steve Jalim
    Steve Jalim about 11 years
    Update, ages on from my original answer: github.com/SmileyChris/django-mailer-2 does support attachments, too
  • Jeewes
    Jeewes almost 10 years
    More info about email backends: docs.djangoproject.com/en/dev/topics/email/#email-backends. Sometimes even simple console backend is enough..
  • nimiq
    nimiq over 8 years
  • Matt
    Matt almost 7 years
    How would you do this if your testing a function which calls send_mail and you therefore can't access mail?
  • pymarco
    pymarco over 6 years
    @MatthewDrill you can still access mail.outbox when send_mail is called in another function.
  • Rob
    Rob over 5 years
    @pymarco If you import mail from core, mail.outbox[0].body will show you the email sent even if the send_mail is performed elsewhere.
  • Overdrivr
    Overdrivr over 5 years
    But is there a way to access the generated email during (automated) testing ?
  • Manel Clos
    Manel Clos about 5 years
    This works perfect even with mass sending using send_messages, here: docs.djangoproject.com/en/2.1/topics/email/…