Setting up a fake SMTP server for testing

A fake SMTP server is frequently a used by those looking to capture and test all emails an application sends and avoid sending emails to real customers. categories: tools

In this article, we’ll walk you through what a fake SMTP server is (with context on why to test emails at all) and show you how to set one up, either as a self-hosted option or using Mailosaur’s managed service.

What is a fake SMTP server?

A fake SMTP server is a server that accepts Simple Mail Transfer Protocol (SMTP) messages but doesn’t send actual emails to outside destinations. Fake SMTP servers are commonly set up for development and testing purposes: as a developer working on an application that sends email, you generally want to double-check all communications that go from your app to your prospects or customers, including any transactional emails, without sending emails to real customers.

Why should I test my emails?

In case you’re wondering why you should test the emails you plan to send, here are just a few examples of scenarios that email testing can avoid:

  • password reset emails with links that don’t work;
  • incorrectly formatted recipient name in the email salutation, or the email address inserted in place of the first name;
  • emails not getting sent at all due to SMTP connection errors;
  • transactional emails from your app ending up in the Gmail spam folder.

…and there are many more potential failure scenarios. Testing email communications is especially important now that we rely on email for authentication, password resets, two-factor authentication setup, and other sensitive functionality.

To mitigate potential email-related issues, some developers simply write more unit tests. But while it might be easy to implement unit tests to check the self-contained functions in your code, testing email functionality can be much more complex: you need to not only check for issues within the body of the email that you’re sending, but also within the code paths that do the sending of each email.

Manually testing email functionality can quickly become tedious, so we strongly recommend implementing comprehensive automated tests for the emails that your application sends.

Why should I set up a fake SMTP server for email testing?

Using an SMTP server, even if it’s a fake one, can be helpful for testing your email functionality.

Using a dedicated SMTP server for testing is usually more reliable for testing emails compared to mocking email-sending libraries and classes: When mocking or stubbing out libraries, it’s possible to make errors in the mocks themselves that would allow bugs to slip in. By contrast, when you test email with a fake SMTP server, you’re following the same code paths as the ones used for sending production emails. You’re thus more likely to preempt unexpected issues in production.

Using Gmail as your development email can hurt deliverability

You might be asking: “So, should I just send emails to my Gmail address as a test?”

We recommend against doing this.

Shocking as it may be, but sending repeated emails to a Gmail inbox, even if it’s your personal address or a dedicated inbox created for testing, can impact your email deliverability across other Gmail addresses (and even other providers). Google and other companies use spam filters based on machine learning, and each incoming email on the entire service can be used as a datapoint for future spam detection. This is good news for you as a user as it helps to reduce the amount of spam. But it’s easy to inadvertently cause emails from your app to get classified as spam because of a few malformed test emails.

Another risk with Gmail addresses lies with using a shared server to send emails for testing purposes. For example, if you’re using an IP address given to you by a hosting provider, and it turns out that the IP was previously used by another customer to send spam, there’s a chance that their past actions will impact the deliverability of your emails. Associating a potentially-malicious IP with your domain can make it hard to ever send email from your domain and have it seem legitimate to email providers.

Having a dedicated fake SMTP server for development and testing purposes that doesn’t communicate with real email providers is thus essential to preserving your email deliverability.

How to set up a fake SMTP server

If you’ve read this far, you’re probably interested in setting up a dedicated SMTP server for your development and testing needs right away.

To give you an idea of the next steps, we’ll walk you through two possible options for setting up a fake SMTP server for development and testing. We’ll first set it up with smtp4dev, an open-source solution, and will then do the same using Mailosaur, a fully hosted option.

Setting up a fake SMTP server with smtp4dev

smtp4dev is an open-source fake SMTP server frequently used for development purposes. The fastest way to get started with this option is to install Docker on your development machine and use the smtp4dev Docker container. Alternatively, if you’re running Linux or Windows, you can download and install an smtp4dev package.

In this example, we use the Docker option for setting up smtp4dev. You will need to ensure that the Docker daemon is running. An easy way to check this is by running docker info in the command line and checking the Server: section. If Docker is installed and running, you should see a summary:

~ % docker info
...
Server:
    Containers: 0
    Running: 0
    Paused: 0
    Stopped: 0
...

To get smtp4dev set up, start the rnwood/smtp4dev:v3 container. When you do this for the first time, Docker will download the image from Docker Hub, and it will be reused next time you launch the smtp4dev image:

~ % docker run --rm -it -p 3000:80 -p 2525:25 rnwood/smtp4dev:v3
    v3: Pulling from rnwood/smtp4dev
    68ced04f60ab: Pull complete
    4ddb1a571238: Pull complete
    94b78a0446e2: Pull complete
    b48f8e1b0b06: Pull complete
    a41ea3d79519: Pull complete
    bbbe93f6aff1: Pull complete
    Digest: sha256:a821221fd4f6e8cf17b371e11d2acc2fcc4ba05125bec827abec7f821b6be9f2
    Status: Downloaded newer image for rnwood/smtp4dev:v3
    smtp4dev version 3.1.3-ci20210316132
    https://github.com/rnwood/smtp4dev
    
    .NET Core runtime version: 3.1.2
    
    
     > For help use argument --help
    
    Using Sqlite database at /smtp4dev/database.db
    
    TLS mode: None
    
    SMTP Server is listening on port 25.
    Keeping last 100 messages and 100 sessions.
    IMAP Server listening on port 143
    Hosting environment: Production
    Content root path: /app
    Now listening on: http://[::]:80
    Application started. Press Ctrl+C to shut down.

Here’s a quick guide on each of the Docker options our command uses:

  • --rm — remove the container when the service finishes running. Next time you start the smtp4dev container it will have a clean state rather than having your data from the previous launch.
  • -it — launch the container interactively (rather than letting it run in the background) and stream its output into the terminal. smtp4dev will run in your current terminal window, and closing the window will stop the smtp4dev process.
  • -p 3000:80 — map port 80 on the container (the standard HTTP server port) to port 3000 on your local machine. The smtp4dev console will be available on http://localhost:3000.
  • -p 2525:25 — map port 25 on the container (the SMTP server port) to port 2525 on your machine. All your tests will need to use port 2525 to connect to smtp4dev.

At this point the application is up and running, and you can visit http://localhost:3000 to view the interface.

smtp4dev's web interface

You can now send your first email to smtp4dev. Our server is running on port 2525, so let’s try connecting to it. We use smtp-cli in this example, but you can use any SMTP client, including the libraries that you use in your application:

~ % ./smtp-cli --verbose --server localhost --port=2525
Connection from [::1]:60618 to [::1]:2525
[220] '4eb77fb909ab smtp4dev ready'
> HELO localhost
[250] 'Nice to meet you'
> QUIT
[221] 'Goodbye'

The output shows that the server is working correctly. Let’s send a simple email to it. We’ll create a data.txt file with the headers and the body of the email:

subject: Dragons

Either dragons should exist completely or fail to exist at all, he felt. A dragon only half-existing was worse than the extremes.

We then send that email to our fake SMTP server:

./smtp-cli --verbose --server localhost --port=2525 --from test@example.org --to test@example.net --data data.txt
Connection from [::1]:60750 to [::1]:2525
[220] '4eb77fb909ab smtp4dev ready'
> HELO localhost
[250] 'Nice to meet you'
> MAIL FROM:<test@example.org>
[250] 'New message started'
> RCPT TO:<test@example.net>
[250] 'Recipient accepted'
> DATA
[354] 'End message with period'
[250] 'Mail accepted'
> QUIT
[221] 'Goodbye'

Great! The email should show up in the smtp4dev web interface.

Test email shows up in the smtp4dev web interface

smtp4dev can be a great tool for development, but make sure you consider its potential limitations before running with it.

First, while it’s possible to run the smtp4dev container locally as we’ve described, it’s not straightforward to make assertions in your unit tests against the contents of the emails that smtp4dev receives.

Second, consider how you’ll run smtp4dev in your continuous integration environment. Without the UI you won’t be able to tell whether the emails preserve your intended format unless you review every test run.

And third, the default configuration might not be representative of what you’ll find in the real world. Make sure to review the smtp4dev configuration instructions and enable settings like SSL/TLS to make your testing more realistic.

Setting up a fake SMTP server with Mailosaur

Mailosaur is a fully managed SMTP service for development and testing, so no local server setup is required. Follow these steps to get started:

  1. Navigate to the Mailosaur website and click Sign Up.
  2. Once you are in the app, you will see a random email address that you can use for testing. Mailosaur's initial screen

Let’s again use smtp-cli to send our test email:

% ./smtp-cli --verbose --port=2525 --from test@example.org --to example@m03agkv6.mailosaur.net --data data.txt

Resolved MX for example@m03agkv6.mailosaur.net: mailosaur.net
Connection from 192.168.178.24:60843 to 35.205.180.144:2525
[220] 'mailosaur.net Ready'
> HELO localhost
[250] 'mailosaur.net'
> MAIL FROM:<test@example.org>
[250] 'sender <test@example.org> OK'
> RCPT TO:<example@m03agkv6.mailosaur.net>
[250] 'recipient example@m03agkv6.mailosaur.net OK'
> DATA
[354] 'Enter mail'
[250] 'Mailosaur'
> QUIT
[221] 'Bye'

The email quickly appears in the Mailosaur interface. We can see that there are multiple warnings that we’ll need to fix before shipping this email to production.

Test email shows up in Mailosaur

Being a hosted service, Mailosaur offers a few additional benefits compared to a basic fake SMTP server:

  1. Mailosaur’s test libraries allow you to make assertions against the contents of an email in your unit tests, both locally and in CI. This is a great option if you’re looking to automate testing as much as possible.
  2. Because Mailosaur is a fully hosted option, you can get started quickly and don’t need to manage SMTP infrastructure in your development or staging environments.
  3. Mailosaur takes the hard work out of email testing by extracting links and images from your email's html and text.

Mailosaur: your all-in-one email testing solution

Since many of your app’s critical user interactions happen through email, we encourage you to test all transactional emails as thoroughly as you would test the other parts of your application.

Using a fake SMTP server is a great starting point for email testing, and we recommend choosing the solution that will let you automate as much of the testing as possible, with minimal setup.

Check out Mailosaur’s guide to email testing for more information.