How to Send an Email with smtplib and a Mock Server in Python

Send an email using smtplib and verify it with a local aiosmtpd mock SMTP server — perfect for testing without a real mail server.

Medium Python 3.9+ Aug 9, 2026 Automation & scripting 12 views 0 copies

Requires third-party packages — install first
pip install aiosmtpd

Python code

37 lines
Python 3.9+
import smtplib
from email.message import EmailMessage
import aiosmtpd.controller as controller
import threading


def handle_message(server, session, envelope):
    print(f"Mock server received message:")
    print(f"From: {envelope.mail_from}")
    print(f"To: {envelope.rcpt_tos}")
    print(f"Subject: {envelope.content.decode()}")
    return '250 OK'


def main():
    # Start mock SMTP server on port 1025
    mock_server = controller.Controller(handler=handle_message, hostname='127.0.0.1', port=1025)
    mock_server.start()
    print("Mock SMTP server started on 127.0.0.1:1025")

    # Create email message
    msg = EmailMessage()
    msg.set_content("Hello, this is a test email sent with smtplib!")
    msg['Subject'] = "Test Email"
    msg['From'] = "sender@example.com"
    msg['To'] = "recipient@example.com"

    # Send email via mock server
    with smtplib.SMTP('127.0.0.1', 1025) as client:
        client.send_message(msg)

    print("Email sent successfully via mock server")
    mock_server.stop()


if __name__ == "__main__":
    main()

Output

stdout
Mock SMTP server started on 127.0.0.1:1025
Mock server received message:
From: sender@example.com
To: recipient@example.com
Subject: Test Email
Email sent successfully via mock server

How it works

aiosmtpd runs a lightweight SMTP server in-process, letting you test email sending without a real mail service. The handle_message callback receives the envelope with sender, recipients, and raw content — here we print them to prove delivery. smtplib.SMTP connects to that local server and send_message handles RFC-compliant formatting automatically. The with block guarantees the connection closes, and EmailMessage gives a clean API for subject, from, and to headers. Stopping the controller shuts down the mock server gracefully after the test.

Common mistakes

  • Forgetting to start the controller before sending — SMTP connection will fail with a connection refused error
  • Not calling `mock_server.stop()` which leaves a zombie port open
  • Confusing `send_message` (accepts EmailMessage) with `sendmail` (needs raw strings)

Variations

  1. Use `smtplib.SMTP_SSL` instead of `SMTP` for real Gmail or Outlook connections over port 465
  2. Replace the mock server with `smtplib.SMTP('smtp.gmail.com', 587).starttls()` for production email delivery

Real-world use cases

  • Writing unit tests for an email notification feature without spamming real addresses.
  • Validating email formatting and SMTP logic in a CI/CD pipeline in isolation.
  • Developing a mail-sending script locally where no corporate SMTP relay is accessible.

Sponsored

Run locally

This sample needs third-party packages, so it cannot run in the browser IDE. Copy the code above, install the packages shown at the top, then run it in your own Python environment.

More from Automation & scripting

Related tutorials and quizzes for this topic.