Don't want to code along? Here is the GitHub repository for what we are building on this post!
Most apps sooner or later will have to deal with sending emails to the user, that being after registration or a specific event. After sending the email, a developer most likely would want to test how the user would receive it or if there is a link, how it reacts within the app. Using a real SMTP server for this can be tricky and could end up spamming a user with emails without needing to. That is why a fake SMTP server can be useful as it can allow you to send emails to a safe box that will trap all these emails and can be fetched if you need to use them to test your flow or content. There are many SaaS (Software as a Service) that can set up this server for you, however, they can be costly and limiting. That is why in this post, I'll be teaching you how to use Mailhog with Docker to create a fake email server and be able to fetch emails so you can use it on your tests.
Creating an Express Server
In this tutorial, I'll use an Express server with Nodemailer since it is my main toolset. Regardless, this method will work for any stack that can use SMTP as a client. First, start a node environment by running:
npm init -y
Then, install the required dependencies:
npm install --save express nodemailer
Next, create a server.js
which will start our server:
const express = require("express");
const app = express();
const port = 3000;
app.get("/", (req, res) => res.send("Hello World!"));
app.listen(port, () =>
console.log(`Example app listening at http://localhost:${port}`)
);
Add a start script in package.json
to start our server:
{
"scripts": {
"start": "node server.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
}
Finally, start the server with:
npm run start
Setting up docker
Setting Mailhog is as easy as using your OS install command. However, if you are sharing your repository with other developers it would be nice to use Docker so that it can work as expected in other machines or CI (Continous Integration). Also, the developer is able to clean the history as needed by just removing the container.
If you don't have docker installed please refer to their site and follow their instruction on how to install for you OS.
Note: If you are using Linux, please install Docker Compose on their docs page
Once you have installed docker create a Dockerfile
. This will fetch a Node.js image and start our server.
FROM node:10
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./
RUN npm install
# Bundle app source
COPY . .
CMD [ "node", "server.js" ]
Also, create a .dockerignore:
node_modules
npm-debug.log
Create a docker-compose.yaml
which will start up our just created Dockerfile and also Mailhog.
version: '2.4'
services:
node:
build:
context: .
dockerfile: Dockerfile
ports:
- '3000:3000'
mailhog:
image: mailhog/mailhog:latest
restart: always
ports:
- 1025:1025
- 8025:8025
Finally, run:
docker-compose up --build
And you will see that our images will be built and our servers will start automatically!
Receiving Emails
To receive emails we just have to send them as we would for a normal SMTP. Go to server.js
and add the following :
const express = require("express");
const nodemailer = require("nodemailer");
const app = express();
const port = 3000;
const transporter = nodemailer.createTransport({
host: "mailhog",
port: 1025
});
app.get("/send_email/:email", (req, res) => {
const { email } = req.params;
const messageStatus = transporter.sendMail({
from: "My Company <company@companydomain.org>",
to: email,
subject: "Hi Mailhog!",
text: "This is the email content",
});
if (!messageStatus) res.json("Error sending message!").status(500);
res.json("Sent!").status(200);
});
// ...
Here we create a Nodemailer transport with Mailhog's default credentials. Also, a GET route that receives an email
parameter to where we want to 'send' it. Now if you head in your browser and type http://localhost:3000/send_email/test@test.com
you will see a 'sent!' JSON response. If you head towards Mailhog's dashboard on [localhost:8025](http://localhost:8025)
, the email we just sent will be visible and you can open it to see any details such as headers, and text content.
Query Mailhog
Querying Mailhog is as easy as doing it for a REST server. To find all the possible routes to query, you can find their Swagger JSON and YAML on their GitHub. To get all the emails from a specific email we use the following GET route http://localhost:8025/api/v2/search
. Since we sent a message to test@test.com
our query will be http://localhost:8025/api/v2/search?kind=to&query=test@test.com
. If you paste this in your browser you'll see the email we just sent!
Awesome! Now you can use this information for testing using Cypress or Jest.
Conclusion
Testing email flows is very important to guarantee a stellar experience for users, especially since these flows can sometimes be fundamental for general functionality like a sign-up confirmation. Using our real STMP server can result in sending a user an email by mistake. Also, by setting a fake SMTP using a SaaS provider like Mailtrap can be expensive. Instead, we can use Mailhog and Docker to create this server quickly that allows us to query for sent emails so we can use them in our tests without dealing with quota or price plans.
For more up-to-date web development content, follow me on Twitter, and Dev.to! Thanks for reading! 😎
Did you know I have a newsletter? 📬
If you want to get notified when I publish new blog posts and receive an awesome weekly resource to stay ahead in web development, head over to https://jfelix.info/newsletter.
Top comments (0)