How to Test

For testing purposes, it might be useful to prevent sending the actual email. This is especially preferable with unit tests. There are several ways to do this.

Note

Red Mail extends email.message.EmailMessage from standard library. You may use its attributes and methods for testing the contents of your messages.

See Email Strucure (MIME) for how Red Mail’s emails are structured.

Using get_message

All of the arguments in method EmailSender.send() are passed to EmailSender.get_message() method which generates the message itself. Therefore, the simplest solution is to use this method instead of EmailSender.send() in tests:

from redmail import EmailSender

# Just put something as host and port
email = EmailSender(host="localhost", port=0)

msg = email.get_message(
    subject='email subject',
    sender="me@example.com",
    receivers=['you@example.com'],
    text="Hi, this is an email.",
)

assert str(msg) == """From: me@example.com
Subject: Some news
To: you@example.com
Message-ID: <167294165062.31860.1664530310632362057@LAPTOP-1234GML0>
Date: Sun, 31 Jan 2021 06:56:46 -0000
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
MIME-Version: 1.0

Hi, nice to meet you.
"""

Mock Server

In case changing to method EmailSender.get_message() is inconvenient or it does not suit to your testing, you may also create a mock SMTP server that imitates an actual SMTP server instance:

class MockSMTP:

    messages = []

    def __init__(self, host, port):
        self.host = host
        self.port = port

    def starttls(self):
        # Called only if use_startls is True
        return

    def login(self, username, password):
        # Log in to the server (if credentials passed)
        self.username = username
        self.password = password
        return

    def send_message(self, msg):
        # Instead of sending, we just store the message
        self.messages.append(msg)

    def quit(self):
        # Closing the connection
        return

Then to use this mock:

from redmail import EmailSender

email = EmailSender(
    host="localhost",
    port=0,
    username="me@example.com",
    password="1234",
    cls_smtp=MockServer
)

email.send(
    subject='email subject',
    sender="me@example.com",
    receivers=['you@example.com'],
    text="Hi, this is an email.",
)

msgs = MockServer.messages
assert msgs == ["""From: me@example.com
Subject: Some news
To: you@example.com
Message-ID: <167294165062.31860.1664530310632362057@LAPTOP-1234GML0>
Date: Sun, 31 Jan 2021 06:56:46 -0000
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
MIME-Version: 1.0

Hi, nice to meet you.
"""]

Note that an instance of MockServer is created for each connection, often per sent email.

Subclass Sender

Another option is to just subclass the sender and change the email sending there:

from redmail import EmailSender

class MockSender(EmailSender):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.messages = []

    def send_message(self, msg):
        self.messages.append(msg)

Then to use this class:

# Just put something as host and port
email = MockSender(host="localhost", port=0)

email.send(
    subject='email subject',
    sender="me@example.com",
    receivers=['you@example.com'],
    text="Hi, this is an email.",
)

msgs = email.messages
assert msgs == ["""From: me@example.com
Subject: Some news
To: you@example.com
Message-ID: <167294165062.31860.1664530310632362057@LAPTOP-1234GML0>
Date: Sun, 31 Jan 2021 06:56:46 -0000
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
MIME-Version: 1.0

Hi, nice to meet you.
"""]