Write Integration Tests Using Mailsac

Mailsac has a REST API that can be leveraged to validate emails are being received with the correct contents. This can be useful for web applications that send customized emails to customers.

This example will demonstrate how to send an email via an SMTP server and validate the email was received. The code used in this example is available on GitHub.

Setup Project Directory

Node version 12 or greater is required for this example due to the use of async and await.

Begin by creating a directory for the project.

mkdir mailsac-tests
cd mailsac-tests
npm init # enter project information, for test command use 'mocha'

Verify package.json looks something like this. The most important part is "scripts": { "test": "mocha" }

{
  "name": "mailsac-integration-tests",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "directories": {
    "test": "test"
  },
  "scripts": {
    "test": "mocha"
  },
  "author": "",
  "license": "ISC"
}

Installing a Test Framework

The test framework for this example is Mocha. It is a test framework for Javascript applications. A test framework will allow us to write tests against our code.

Setup Test Directory

These directions are inspired by the Mocha Getting Started Guide.

cd mailsac-tests
npm install --save-dev mocha
mkdir test
$EDITOR test/test.js # or open with your favorite editor

Write a Test to Test For Truth

This section is for people who are new to Mocha and test frameworks. Skip ahead if you know how to write tests.

Mocha follows a Behavior Driven Development (BDD) model of testing. This allows for human readable descriptions of software behavior. The first line of the code shows exactly what the following tests will test (ie describe("tests truth"). The following lines state the expected outcome of the test (ie it(true equals true).

This test uses Node’s built in assert. The first parameter passed to assert() is checked for true. If it is true, the assert passes. If the value is false, it throws an exception. In this example, we assert that true is true and that false is equal to false.

Create a describe block in test/test.js
describe("tests truth", () => {
    it('true equals true', function() {
        assert(true); // assert checks for truth
    });
    it('false equals false', () => {
        // assert equal checks the first and second parameter are equal
        assert.equal(false,false);
    });
})

This code can be run and the test should pass. Run the test with npm test . Confirm the test passed.

Congratulations! You have a working test.

Write an Integration Test

In this example, an email will be sent to Mailsac using the ubiquitous Javascript SMTP library NodeMailer, then the test framework will call Mailsac to validate that the email was received.

Install Dependencies

There are a few Node modules that will make sending an email and interacting with the Mailsac REST API easier. NodeMailer will be used to send the email via SMTP. SuperTest will be used to call the Mailsac API.

npm install

Integration Test Example

Back in the text editor, open / create the file test.js in the test folder. Copy and paste the following code or see GitHub for the complete example.

Import required modules
const assert = require("assert");
const nodemailer = require("nodemailer");
const request = require("supertest");
Configure SMTP settings and Mailsac Credentials
// Generated by mailsac. See https://mailsac.com/api-keys
const mailsacAPIKey = "";
// Mailsac email address where the email will be sent
const mailsacToAddress = "[email protected]";
// Username for smtp server authentication
const smtpUserName = "";
// Password for smtp server authentication
const smtpPassword = "";
// hostname of the smtp server
const smtpHost = "";
// port the smtp is listening on
const smtpPort = 587;
Create wait Function

The test will contact the Mailsac API multiple times while waiting for the email to arrive. This function will be used later in a for loop while waiting for the email to arrive.

const wait = (millis) => new Promise((resolve) => setTimeout(resolve, millis));
Add a Describe block with Timeout and Test Cleanup

The describe callback describes in human readable terms what the test is going to do. The increased timeout is required because the default timeout for Mocha is 2 seconds. The test email will likely not arrive that quickly. The afterEach section is used to delete all messages after the test runs. This prevents a leaky test.

describe("send email to mailsac", function () {
  this.timeout(50000); // test can take a long time to run. This increases the default timeout for mocha

  /* delete all messages in the inbox after the test runs to prevent leaky tests.
       This requires the inbox to private, which is a paid feature of Mailsac.
       The afterEach section could be omitted if using a public address
    */
  afterEach(() =>
    request("https://mailsac.com")
      .delete(`/api/addresses/${mailsacToAddress}/messages`)
      .set("Mailsac-Key", mailsacAPIKey)
      .expect(204)
  );
});
Add it block and NodeMailer Configuration

The it interface describes what the test will do "sends email with link to example.com website“. The transport variable is used to store the configuration of the SMTP server.

result = await transport.sendMail({… Attempts to send the email and capture the result.

describe("send email to mailsac", function () {
...

  it("sends email with link to example.com website", async () => {
    // create a transporter object using the default SMTP transport
    const transport = nodemailer.createTransport({
      host: smtpHost,
      port: smtpPort,
      auth: {
        user: smtpUserName,
        pass: smtpPassword,
      },
    });
    // send mail using the defined transport object
    const result = await transport.sendMail({
      from: smtpUserName, // sender address
      to: mailsacToAddress, // recipient address
      subject: "Hello!",
      text: "Check out https://example.com",
      html: "Check out <a href https://example.com>My website</a>",
    });

    // logs the messageId of the email, confirming the
    // email was submitted to the smtp server
    console.log("Sent email with messageId: ", result.messageId);
});
Add Loop to Check Mail

This section of code uses a for loop and a http library (supertest) to check if the message has arrived at Mailsac. The test uses the Mailsac API endpoint /api/addresses/{email}/messages which lists all messages in an inbox.

describe("send email to mailsac", function () {
...

  it("sends email with link to example.com website", async () => {
...
    // Check email in the inbox 10x, waiting 5 secs in between. Once we find mail, abort the loop.
    let messages = [];
    for (let i = 0; i < 10; i++) {
      // returns the JSON array of email message objects from mailsac.
      const res = await request("https://mailsac.com")
        .get(`/api/addresses/${mailsacToAddress}/messages`)
        .set("Mailsac-Key", mailsacAPIKey);

      messages = res.body;
      if (messages.length > 0) {
        break;
      }
      await wait(4500);
    }
  });
});
Add Assertion to Check for Link in the Message

assert is used twice. First, to check to see if any messages were fetched from the Mailsac inbox. This checks the length of the messages array to see if any messages were received. The second assert is used to check for a link to http://example.com .

describe("send email to mailsac", function () {
  // ...
  it("sends email with link to example.com website", async () => {
    // ...
    let messages = [];
    for (let i = 0; i < 10; i++) {
      // ... await get messages from mailsac
    }
    assert(messages.length, "Never received messages!");

    // After a message is retrieved from mailsac, the JSON object is checked to see if the link was parsed from the email and it is the correct link
    const link = messages[0].links.find((l) => "https://example.com");
    assert(link, "Missing / Incorrect link in email");
  });
});
Run Test

At this point the test code is complete and can be run using npm test. If all went well the following output is written to the console.

npm test

Run Test Using GitHub Repository

If you are having some trouble with the tests, I recommend downloading the code from GitHub and running it.

git clone https://github.com/mailsac/mailsac-integration-test-examples.git
cd mailsac-integration-test-examples
npm install
$EDITOR test/test.js # or open with your favorite editor

Edit the test.js and fill in your SMTP settings and Mailsac API key

const mailsacAPIKey = ""; // Generated by mailsac. See https://mailsac.com/api-keys
const mailsacToAddress = "[email protected]"; // Mailsac email address where the email will be sent
const smtpUserName = ""; // Username for smtp server authentication
const smtpPassword = ""; // Password for smtp server authentication
const smtpHost = ""; // hostname of the smtp server
const smtpPort = 587; // port the smtp is listening on

Run test running the command npm test in your terminal.

Next Steps

The example above can be used as part of your team’s quality assurance process. A real world example would be validating password reset links to customers. This test could be used to validate that when a customer requests a password reset an email is sent to them containing the correct password reset link.

If you have any questions about this example or want to talk about other implementations reach out to our community at https://forum.mailsac.com .

Improved SMTP Relay Logs

The Recent Activity Log now includes detailed sender and delivery information for outbound email. This empowers customers to diagnose sending issues.

Successful Email Log Entry

The screenshot below shows a successful email delivery sent from [email protected] to [email protected]. The email message id, number of delivery attempts, rejection status, and time stamps are included in the logs

Failed Email Log Entry

The screenshot below shows a failed email delivery from [email protected]. The message id, number of attempts, timestamp, and error message are provided.

This error message indicates that the recipients domain no-where-no-where-no-where.info does not exist.

Industry Standard API Documentation

Mailsac API Documentation has been converted over to use OpenAPI.

Code Examples

The new format gives developers and quality assurance testers code samples for common programming languages (curl, Node, Python, PHP, Go, etc).

Explore the API Using Swagger UI

The API can be explored without writing any code using the Swagger UI Explorer. All that is needed to get started is a free Mailsac API Key (requires sign up).

In the Swagger UI Explorer, choose the Authorize button and enter your Mailsac API Key


Use the “Try it out” button to interact with any of the REST API endpoints. A curl example, request URL, response body, and headers are provided.

Organize Your Team’s Email Accounts for Testing

Stop using mailing lists, shared inboxes, and GMail aliases for email testing. Things get messy fast, and often they aren’t easily shared with a team.

Use Mailsac for test email accounts. Make private or shared email addresses for each environment and test scenario. It’s disposable mail designed for QA.

Mailsac helps software teams organize their test accounts in 2 steps.

1. Setup a custom domain.
Pick any available subdomain of *.msdc.co.
Or, add a few DNS entries and use your existing domain, like test.example.com.

2. Start getting email immediately. You are ready to start testing! No need to create any inboxes.
Send an email to any address at your Mailsac domain.
Then check the mail.

If you want, an email inbox can be saved for reuse.

Add a comment so you and your team can see what that address is used for (“test account with admin role for dev and uat environments”).

Advanced: Catch-All

Enable a catch-all under the custom domain’s settings. It’s useful for one-off signup tests. A Catch-All inbox receives all email sent to a specific domain (for non-reserved addresses).

how to enable a sisposable email catch-all inbox

Now, any email address under your mailsac domain will be routed to the catch-all.

Get Started Receiving Mailsac Email For Free

Public inboxes for any @mailsac.com email address are free.

Custom domain plans start at $12 per month, or $10 per month when paid yearly.

We serve small dev shops, financial institutions, and government project teams with the same friendly support.

Advanced Disposable Mail Features

Create a free account to get started

New Features Available On All Plans

All of our plans now included additional features. These features will help quality assurance teams and individual developers test sending and receiving of email, and easily share non-production email accounts.

To see a complete listing of feature visit see our pricing page.

We are in the process of rolling out these features to our existing customers. Please contact [email protected] if you need access to the features ahead of our scheduled roll out.

Free Plan

Old PlanNew Plan Feature / Limit
N/AEmail Capture
N/AWebhook Forwarding (requires private inbox)
N/AWebSocket Forwarding (requires private inbox)
N/APurge Inbox (requires private inbox)
N/ASlack Webhooks (requires private inbox)
N/ACommunity Support

Indie Plan

Old PlanNew Plan Feature / Limit
10 Private Addresses50 Private Addresses
0 Outgoing Messages500 Outgoing Messages (non-recurring)
N/AEmail Capture
N/AMessage Logs (15 minutes)
N/ADelete all messages by domain

Business Plan

Old PlanNew Plan Feature / Limit
50 Private Addresses250 Private Addresses
0 Outgoing Messages2,500 Outgoing Messages (non-recurring)
N/AWebsite Login using API Key (2 users)
N/AMessage Logs (1 month)

Enterprise Plan (New Plan)

New and Improved SMTP Header REST API Endpoint

Devs and Quality Assurance Testers Can Easily Validate Mail Headers

The SMTP header endpoint provides quality assurance testers with the option to view an email message’s SMTP headers in parsed formats that easily integrate with automated testing frameworks.

Problem

Developers and QAs are often asked to validate contents of emails. This can include from address, links, and subject. For many organizations this can be a manual process of checking the email and validating if the test criteria has been met.

Solution

Mailsac’s new message header endpoint provides SMTP headers in 3 formats:

1. JSON object format, grouped by lowercased header key. This format is easily consumed by industry standard tools such as Selenium.

{
  "received": [
    "from 107.174.234.77 by frontend1-172-31-29-224 via 172.31.42.57 with HTTP id 8m7iqeiZKJ3MzwTwUQlU for <[email protected]>; Mon Dec 24 2018 15:29:06 GMT+0000 (Coordinated Universal Time)",
    "from 107.174.234.77 by smtp-in2-172-31-42-57 via 172.31.23.10 (proxy) with SMTP id 8m7iqeiZKJ3MzwTwUQlU for <[email protected]>; Mon, 24 Dec 2018 15:29:06 UTC",
  ],
  "from": [
    "[email protected]"
  ],
  "to": [
    "[email protected]"
  ],
  "subject": [
    "invitation to collaborate"
],
  "date": [
    "Mon, 24 Dec 2018 15:29:06 +0000"
  ]
}

2. Ordered JSON array format. This formats pre-parses the headers, but maintains the original order, while still handling duplicate headers such as Received.

?format=ordered-json

[
  {
    "name": "received",
    "value": "from 107.174.234.77 by frontend1-172-31-29-224 via 172.31.42.57 with HTTP id 8m7iqeiZKJ3MzwTwUQlU for <[email protected]>; Mon Dec 24 2018 15:29:06 GMT+0000 (Coordinated Universal Time)"
  },
  {
    "name": "received",
    "value": "from 107.174.234.77 by smtp-in2-172-31-42-57 via 172.31.23.10 (proxy) with SMTP id 8m7iqeiZKJ3MzwTwUQlU for <[email protected]>; Mon, 24 Dec 2018 15:29:06 UTC"
  },
...
  {
    "name": "to",
    "value": "[email protected]"
  },
]

3. Plaintext original format. This format is useful when you are interested in parsing or inspecting the email headers yourself, and do not wish to download the entire message.


?format=plain

Received: from 107.174.234.77 by frontend1-172-31-29-224 via 172.31.42.57 with HTTP id 8m7iqeiZKJ3MzwTwUQlU for <[email protected]>; Mon Dec 24 2018 15:29:06 GMT+0000 (Coordinated Universal Time)
Received: from 107.174.234.77 by smtp-in2-172-31-42-57 via 172.31.23.10 (proxy) with SMTP id 8m7iqeiZKJ3MzwTwUQlU for <[email protected]>; Mon, 24 Dec 2018 15:29:06 UTC
...
To: [email protected]

“We are currently using the REST API headers endpoint in support between our own microservices. Our POP3 server fetches headers of message to implement the POP3 TOP command.” — Michael Mayer, Partner Forking Software LLC

Getting Started

The message header endpoint /api/messages/:messageId/headers is available on all Mailsac plans (including our free tier). See our API Specification for more information.

This code example could can be modified to view the headers for the first email message on an inbox [email protected]. Make sure to insert your API Key and change the email address to an email address you which is public or reserved by your account.

const superagent = require('superagent') // npm install superagent

const mailsac_api_key = 'YOUR_API_KEY_HERE' // change this!

superagent
  .get('https://mailsac.com/api/addresses/[email protected]/messages')
  .set('Mailsac-Key', mailsac_api_key)
  .then((messages) => {
      const messageId = messages.body[0]._id
      superagent
          .get('https://mailsac.com/api/addresses/[email protected]/messages/' + messageId + '/headers')
          .set('Mailsac-Key', mailsac_api_key)
           .then((response) => {
               console.log(response.body)
           })
  })
  .catch(err => console.error(err))

/**
{
  received: [
    'from [ by fireroof via ::1 with HTTP id bo4xdVji_oqEixBO0gGLbvIoe for <[email protected]>; Wed, 28 Oct 2020 23:05:29 GMT',
    'from [ fireroof with SMTP id bo4xdVji_oqEixBO0gGLbvIoe for <[email protected]>; Wed, 28 Oct 2020 16:05:29 PDT'
  ],
  'x-mailsac-inbound-version': [ '' ],
  date: [ 'Wed, 28 Oct 2020 16:05:29 -0700' ],
  to: [ '[email protected]' ],
  from: [ '[email protected]' ],
  subject: [ 'test Wed, 28 Oct 2020 16:05:29 -0700' ],
  'message-id': [ '<20201028160528.2893005@fireroof>' ],
  'x-mailer': [ 'swaks v20190914.0 jetmore.org/john/code/swaks/' ]
}
**/

Multi-User Login using API Credentials, For Team Collaboration

Update: April 2021 – Multi-User login is now called “Sub-Accounts”

Named API Keys can now be used as website authentication.

Custom domains and Private Addresses have been great for quality assurance teams to conduct end to end automated testing of email. But sometimes interacting with an REST API can be a lot of overhead for non-repeating tasks. API Credentials can now be used to login to the website.

All private addresses and custom domains associated with the primary account will be visible from the website for API users. The permissions for API users are the same as API keys.

Quality assurance teams often share credentials of test accounts for the web application they are testing. These test accounts might to be associated with an email provisioned by their IT department or the QA tester’s personal email. Mailsac private domains allow the test accounts to be created in an an environment all members of the QA team have access to.

This feature allows teams to work together in the Mailsac platform. There is no longer a need to for each person to have their own Mailsac account. A named API Key can be created for each person. That API key can be used to interact with the REST API and the website. As a result, password resets and transaction emails sent to a Mailsac private domain can be accessed by any member of the QA team.

“Internally we have used Mailsac for collaboration. Being able to share a private address or domain allows my team members to see exactly what I am seeing. This feature allows our customers to do the same with their own private domains and addresses” Michael Mayer – Member – Forking Software LLC

Getting started is as easy as provisioning a new set of API credentials and enabling the website login on the API Key. This can be done the the Dashboard and selecting API Credentials & Users

Enable Website Login

We will be rolling this feature out to our Business and Enterprise Plans in the next couple weeks. If you have an immediate need for this feature we can enable it on your account. Contact [email protected] to get early access to this feature on you Business or Enterprise Plan.

Retiring TLS 1.0 and TLS 1.1

Our REST APIs and website will require the use of TLSv1.2 on October 24, 2020. TLS 1.2 was published as RFC 5246 in 2008.

All major web browsers (Chrome, Edge, Internet Explorer, Firefox, and Safari) have already disabled TLS 1.0 and TLS 1.1. Most modern programming languages have support for TLS 1.2.

Integrations written in Java 6 and Python 2.6 do not have TLS 1.2 support.

References

Announcing the Email Capture Service

With email capture, Quality Assurance teams testing email delivery can easily see the exact emails customers will receive.

The new Email Capture Service is an SMTP server that accepts all mail regardless of the To and From address. It is similar to Mailsac’s existing disposable mail, with some key differences. The capture service acts like a fake outbound relay, rather than a fake inbound relay.

Messages sent via capture.mailsac.com are available for viewing from the Mailsac Website or REST API. This allows Quality Assurance teams to validate that the message was sent and the contents of the message.

Web applications frequently send transactional emails to users. During development and User Acceptance Testing, sending email is disabled in the application – so emails are not accidentally sent to customers or fake test addresses. Sending lots of bounces from a development application is a great way to get your domain blacklisted, greylisted, or onto a spam list.

Send via capture.mailsac.com instead

Web applications can be configured to use the Email Capture Service as their SMTP server. Emails sent by the application can then be viewed by developers and testers.

Customers have been sending test emails to hosted custom domains at Mailsac for years. The email capture services simplifies the process of sending emails to Mailsac by allowing developers to relay through Mailsac without any additional email infrastructure.

Michael Mayer – LLC Member at Forking Software LLC.

Most customers can get started sending through the Email Capture service instantly.
Wherever you input SMTP credentials in your application, change to the following:

  • Server Name: capture.mailsac.com
  • Port: 5587
  • Use Secure Connection: Yes (TLS)
  • User Authentication – Username: Mailsac account username *
  • User Authentication – Password: any Mailsac API Key for the account

Any email sent to the capture server will be available at the TO address in Mailsac’s UI or API.

* a private address or custom domain address may be used if your email library does not support a separate FROM address for the username.

Further Reading