Testing emails in Django

27 Jun · by Tim Kamanin · 2 min read

Sending email messages from a web app often seems like throwing stones into a black hole. You create a message, pass it to a mail send function and hope for the best. You don't control an inbox and mail server, so this whole process happens somewhere in between, and you hope it just works.

I think it's time to change that. Email messages can and should be tested like any other piece of a web app. And luckily, Django makes this process super easy.

To send an email message in Django, you can use various email backends: regular SMTP server, GMAIL, or any other service like mailgun or mailchimp.

However, when we run a test, Django's test runner automatically redirects all Django-sent email to a dummy outbox, which lets you test every aspect of sending an email - from the number of messages sent to the contents of each message.

Meet django.core.mail.outbox

During tests, every outgoing email instead of being sent to the outer world is saved in django.core.mail.outbox. Thus we get a virtual inbox that we can quickly inspect.

For example, you can check the number of messages in the box:

from django.core import mail

print(len(mail.outbox))

Or you can grab an email and check its contents:

from django.core import mail

first_message = mail.outbox[0]
print(first_message.subject)
print(first_message.body)

Having these possibilities, you can write tests that verify email contents and delivery.

Here's a quick example of an email test written for Pytest:

from django.core import mail

def test_send_mail():
   # Use Django send_mail function to construct a message
   # Note that you don't have to use this function at all.
   # Any other way of sending an email in Django would work just fine. 
   mail.send_mail(
        'Example subject here',
        'Here is the message body.',
        'from@example.com',
        ['to@example.com']
    )

    # Now you can test delivery and email contents
    assert len(mail.outbox) == 1, "Inbox is not empty"
    assert mail.outbox[0].subject == 'Subject here'
    assert mail.outbox[0].body == 'Here is the message.'
    assert mail.outbox[0].from_email == 'from@example.com'
    assert mail.outbox[0].to == ['to@example.com']

That's all. I hope you've learned something new. If yes, please share this post as much as you can. Thank you for the support!

Want to get more 🔥 tips like this one?

Subscribe to get notified about new dev tutorials