Testing email sending

Any tips on testing email sending? Other than maybe creating a gmail account, especially for receiving those emails?

I would like to, maybe, store the emails locally, within a folder as they are sent.

Answers


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!


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.',
            'from@example.com', ['to@example.com'],
            fail_silently=False)
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(mail.outbox[0].subject, 'Subject here')

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.', 'from@example.com', ['to@example.com'])
    assert len(mail.outbox) == 1

def test_send_again(self):
    mail.send_mail('subject', 'body.', 'from@example.com', ['to@example.com'])
    assert len(mail.outbox) == 1

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.


Django also has an in-memory email backend. More details in the docs under In-memory backend. This is present in Django 1.6 not sure if it's present in anything earlier.


Patching SMTPLib for testing purposes can help test sending mails without sending them.


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


Using the file backend works well, but I find it a little a cumbersome to poke around the filesystem to look at emails. You could use mailcatcher, https://github.com/sj26/mailcatcher, to capture emails and display them in a web UI.

To use mailcatcher with Django you'll need to add something like the following to your settings.py:

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = '127.0.0.1'
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
EMAIL_PORT = 1025
EMAIL_USE_TLS = False

Why not start your own really simple SMTP Server by inherit from smtpd.SMTPServer and threading.Thread:

class TestingSMTPServer(smtpd.SMTPServer, threading.Thread):
    def __init__(self, port=25):
        smtpd.SMTPServer.__init__(
            self,
            ('localhost', port),
            ('localhost', port),
            decode_data=False
        )
        threading.Thread.__init__(self)

    def process_message(self, peer, mailfrom, rcpttos, data, **kwargs):
        self.received_peer = peer
        self.received_mailfrom = mailfrom
        self.received_rcpttos = rcpttos
        self.received_data = data

    def run(self):
        asyncore.loop()

process_message is called whenever your SMTP Server receive a mail request, you can do whatever you want there.

In the testing code, do something like this:

smtp_server = TestingSMTPServer()
smtp_server.start()
do_thing_that_would_send_a_mail()
smtp_server.close()
self.assertIn(b'hello', smtp_server.received_data)

Just remember to close() the asyncore.dispatcher by calling smtp_server.close() to end the asyncore loop(stop the server from listening).


My solution is write content to a html file. This way can help you see how email look like. I leave it here htmlfilebased.EmailBackend.

Other tip: You can use django email template editor which can help you edit your email template with zero inline css.


If you have a TomCat server available, or other servlet engine, then a nice approach is "Post Hoc" which is a small server that looks to the application exactly like a SMTP server, but it includes a user interface that allows you to view and inspect the email messages that were sent. It is open source and freely available.

Find it at: Post Hoc GitHub Site

See the blog post: PostHoc: Testing Apps that Send Email


https://websocket.email provides a simple way to test email sending with minimal setup (you don't even need an account).


Tying a few of the pieces here together, here's a straightforward setup based on filebased.EmailBackend. This renders a list view linking to the individual log files, which have conveniently timestamped filenames. Clicking a link in the list displays that message in the browser (raw):

Settings

EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
EMAIL_FILE_PATH = f"{MEDIA_ROOT}/email_out"

View

import os

from django.conf import settings
from django.shortcuts import render

def mailcheck(request):

    path = f"{settings.MEDIA_ROOT}/email_out"
    mail_list = os.listdir(path)

    return render(request, "mailcheck.html", context={"mail_list": mail_list})

Template

{% if mail_list %}
  <ul>
  {% for msg in mail_list %}
    <li>
      <a href="{{ MEDIA_URL }}email_out/{{msg}}">{{ msg }}</a>
    </li>
  {% endfor %}
  </ul>
{% else %}
  No messages found.
{% endif %}

urls

path("mailcheck/", view=mailcheck, name="mailcheck"),

Use Maildump.

https://github.com/ThiefMaster/maildump

MailDump is a python-based clone of the awesome MailCatcher tool. Its purpose is to provide developers a way to let applications send emails without actual emails being sent to anyone. Additionally lazy developers might prefer this over a real SMTP server simply for the sake of it being much easier and faster to set up.

However it requires Python 2.


Need Your Help

ggplot2: facet_wrap strip color based on variable in data set

r ggplot2 facet-wrap

Is there a way to fill the strips of facets created with facet_wrap based on a variable supplied with the data frame?