DEV Community

Local Email Testing with Python and Mailpit

I'm currently building an app that automates the logistics of tech conferences. It generates certificates of participation for both attendees and speakers and also takes care of sending invitations to prospective presenters. Since it emails multiple recipients, the question arises: in a development environment, how do you test email sending without using real accounts?

In this tutorial, you'll learn how to configure a fake SMTP server and run email tests for Python apps.

Configure a Local SMTP Server

I'm using Mailpit, an Open Source email testing tool. It can be installed following the instructions in the Installation section of the official repository, or by using Docker.

To ensure your data survives a container restart, run the Docker container with a volume to enable persistence:

docker run -d \
  --name mailpit \
  -p 1025:1025 \
  -p 8025:8025 \
  -v $(pwd)/mailpit-data:/data \
  axllent/mailpit
Enter fullscreen mode Exit fullscreen mode

The server listens for SMTP traffic on port 1025, while the web-based dashboard is accessible via port 8025.

Running Tests for Python

Let's create a script to test our email logic. The script will perform the following tasks:

  • Create a list of random names and emails using Faker
  • Construct MIME headers (From, To, Subject)
  • Render the HTML body
  • Establish and SMTP connection and transmit the data

Create a recipient list.

First, we generate a list of participants.

from faker import Faker

fake = Faker('en_US')

if __name__ == "__main__":
    participants = [(fake.name(), fake.ascii_company_email()) for _ in range(10)]
Enter fullscreen mode Exit fullscreen mode

The generated data will look like this:

Name                      | Email                         
-------------------------------------------------------
Jessica Powell            | ryan40@atkinson.com           
Chelsey Glover            | pstevens@hurst.com            
Sheryl Williams           | kenneth61@williams-jacobson.com
Paula Boyd                | larsontheresa@dean.com        
Maxwell Kelly             | justinestrada@willis.org      
Carl Morrow               | pmorris@cross.biz             
David Webb                | abigailfields@holt.com        
Tyler Wolfe               | williamsanna@martinez.info    
Joshua Medina             | williamsrodney@medina.biz     
Mrs. Donna Butler         | williamsmartin@eaton.com
Enter fullscreen mode Exit fullscreen mode

Construct MIME Headers

We use Python's built-in email.mime library to structure the message.

...
from email.mime.multipart import MIMEMultipart

def send_simple_email(recipient_email, recipient_name):
    SENDER_EMAIL = "hello@name.com"

    msg = MIMEMultipart()
    msg['From'] = SENDER_EMAIL
    msg['To'] = recipient_email
    msg['Subject'] = f"Invitation: {recipient_name}"
Enter fullscreen mode Exit fullscreen mode

Render the HTML body

We attach the HTML content to our MIME message.

...
from email.mime.text import MIMEText

def send_simple_email(recipient_email, recipient_name):
    ...

    html_body = f"""
    <html>
        <body style="font-family: sans-serif;">
            <h2 style="color: #2c3e50;">Hello, {recipient_name}!</h2>
            <p>You are formally invited to participate as a speaker at our next event.</p>
            <p>This is a test email captured locally by <strong>Mailpit</strong>.</p>
        </body>
    </html>
    """
    msg.attach(MIMEText(html_body, 'html'))
Enter fullscreen mode Exit fullscreen mode

Establish SMTP connection and transmit email data

Finally, we connect to the local Mailpit server and send the message.

import smtplib
...

def send_simple_email(recipient_email, recipient_name):
    ...

    SMTP_SERVER = "localhost"
    SMTP_PORT = 1025

    try:
        with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
            server.send_message(msg)
            return True
    except Exception as e:
        print(f"❌ Error: {e}")
        return False

if __name__ == "__main__":
    ...
    print(f"\n📧 Starting email delivery to {len(participants)} recipients...")

    for name, email in participants:
        if send_simple_email(email, name):
            print(f" ✅ Sent: {email}")

    print("\n🚀 Check your emails at: http://localhost:8025")
Enter fullscreen mode Exit fullscreen mode

The Complete Script

Here is the full implementation:

import smtplib
from faker import Faker
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

fake = Faker('en_US')

def send_simple_email(recipient_email, recipient_name):
    SENDER_EMAIL = "hello@name.com"

    msg = MIMEMultipart()
    msg['From'] = SENDER_EMAIL
    msg['To'] = recipient_email
    msg['Subject'] = f"Invitation: {recipient_name}"

    html_body = f"""
    <html>
        <body style="font-family: sans-serif;">
            <h2 style="color: #2c3e50;">Hello, {recipient_name}!</h2>
            <p>You are formally invited to participate as a speaker at our next event.</p>
            <p>This is a test email captured locally by <strong>Mailpit</strong>.</p>
        </body>
    </html>
    """
    msg.attach(MIMEText(html_body, 'html'))

    SMTP_SERVER = "localhost"
    SMTP_PORT = 1025

    try:
        with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
            server.send_message(msg)
            return True
    except Exception as e:
        print(f"❌ Error: {e}")
        return False

if __name__ == "__main__":
    participants = [(fake.name(), fake.ascii_company_email()) for _ in range(10)]


    print(f"\n📧 Starting email delivery to {len(participants)} recipients...")

    for name, email in participants:
        if send_simple_email(email, name):
            print(f" ✅ Sent: {email}")

    print("\n🚀 Check your emails at: http://localhost:8025")
Enter fullscreen mode Exit fullscreen mode

Viewing the Results

After running the script, navigate to http://localhost:8025 in your browser. You will find the Mailpit dashboard with an inbox containing all the successfully intercepted test emails.

Mailpit Dashboard

Now you can safely test email features before deploying to production.

Top comments (0)