Email testing means proving four separate things before real users see a message: your code sends the right email to the right person, it renders correctly in the clients people use, its links and tracking work, and mailbox providers deliver it to the inbox. Most teams test only the first, by sending a test email to themselves. This guide covers the whole workflow: how to send test emails without hurting your reputation, which email testing tools do what (Mailpit, Mailtrap, Litmus and others), rendering previews and preview text, spam and link checks, automated end-to-end tests, and safe testing in production.

What email testing has to cover

LayerThe bug it catchesWhere to test
LogicWrong recipient, duplicate sends, missing email after signupUnit and integration tests
ContentEmpty {{first_name}}, wrong locale, stale copyTemplate render tests, previews
RenderingBroken layout in Outlook, unreadable dark modeClient previews, real inboxes
Links and tracking404 links, tokens that never work, broken unsubscribeLink checks, E2E clicks
AuthenticationSPF/DKIM/DMARC fail, misaligned From domainMessage headers, DNS checks
Delivery and placementSpam folder, throttling, silent bouncesSeed tests, delivery webhooks

No single tool covers every row. A capture tool such as Mailpit handles the first four in development. Real inboxes and seed tests cover the last two.

How to send a test email safely

The quickest test is a manual one: trigger the flow and read the result in an inbox you own. Do it with some discipline:

  • Send only to addresses you control. Use a small set of test mailboxes at Gmail, Outlook.com, Yahoo and iCloud, plus one on your company domain. Don't guess addresses, and never use a customer list.
  • Don't use made-up domains for real sends. RFC 2606 reserves example.com, .test, .example and .invalid for documentation and testing. They are fine inside a capture tool. Sent through a real provider, they produce bounces, and bounces count against your sender reputation.
  • Use the production sending path (same From domain, same provider, same templates) for at least the final check. A test sent from a personal Gmail tells you nothing about your DKIM.
  • Label tests. Prefix subjects with [Test] so nobody forwards one as real.

A useful test email example has realistic content, not "test 123". Spam filters and preview panes behave differently on near-empty messages:

Subject: [Test] Your order #1042 has shipped
Preview: Track your package and see the delivery estimate.

Hi Sam,

Good news: order #1042 left our warehouse today.
Estimated delivery: Thursday.

Track your package: https://shop.example.com/orders/1042

Questions? Just reply to this email.

Fill every variable with long, short and missing values: a 40-character surname, a name with an apostrophe, an empty first name. Most rendering bugs live in those edge cases.

Email testing tools for development: capture, don't deliver

In development and CI, nothing should reach a real mailbox. Point your app's SMTP settings at a capture server instead. It accepts everything, delivers nothing, and lets you inspect each message.

Mailpit

Mailpit describes itself as "a small, fast, low memory, zero-dependency, multi-platform email testing tool & API for developers." It is actively maintained: v1.33.1 was released on 7 November 2026. It accepts SMTP on port 1025 and serves a web UI on port 8025:

docker run -d --restart unless-stopped --name=mailpit \
  -p 8025:8025 -p 1025:1025 axllent/mailpit

Beyond capture, it has an HTML check that scores client compatibility, a link check, spam scoring through SpamAssassin or Rspamd, a REST API for tests, tagging, and a "chaos" mode that returns configurable SMTP errors so you can test retry handling.

MailHog

MailHog was the default capture tool for years and uses the same ports, so old tutorials still mention it. Its latest release, v1.0.1, dates from August 2020, and Mailpit's README describes it as "no longer maintained." For new setups, use Mailpit.

Mailtrap Email Sandbox

A hosted option. Mailtrap's sandbox is a fake SMTP server whose "emails are captured in a secure inbox." It adds HTML validation, spam score analysis and SDKs for most languages. It suits teams that want a shared inbox in the cloud rather than a container per developer.

Framework test transports

For unit tests you often don't need SMTP at all:

  • Rails: delivery_method = :test collects mail in ActionMailer::Base.deliveries, and assert_emails 1 { ... } works with both deliver_now and deliver_later.
  • Django: the test runner swaps in the in-memory backend, and sent messages appear in django.core.mail.outbox.
  • Nodemailer: createTransport({ jsonTransport: true }) returns the message as JSON instead of sending it.

Provider test mode

Some email APIs let you exercise the real API without delivering. In Volanea, a key that starts with sk_test_ renders and logs the message but never delivers it, so you can test the real request schema, templates and validation errors in CI.

ToolTypeBest forCost model
MailpitSelf-hosted capture (Docker, binary)Local dev, CI, API assertionsFree, open source
MailHogSelf-hosted captureLegacy setups onlyFree, unmaintained
Mailtrap Email SandboxHosted captureShared team inboxesSee Mailtrap's pricing
Framework test transportsIn-processUnit testsFree
Litmus, Mailgun InspectClient rendering previewsDesign QA across clientsCommercial (see vendor pricing)

Email previews: rendering across clients

Capture tools show you the HTML. They do not show you how Outlook for Windows, Gmail on Android or Apple Mail in dark mode will display it. For that you have two routes.

Paid preview services. Litmus, now part of Validity, offers previews on "100+ popular email clients," dark mode included, plus pre-send checks of "links, images, tracking," accessibility checks, and spam testing across "25+ spam filters." Mailgun Inspect, "built with the best of Email on Acid," also offers previews on "100+ clients and devices" and automated accessibility checks. Both are commercial products; check current pricing on their sites.

Testing email across clients for free. Send the real message to your own accounts and open it in the official apps: Gmail web and mobile, Outlook web plus a desktop Outlook install, Apple Mail on iOS and macOS. Check each one in light mode, dark mode, at a narrow width, and with images blocked. It is slower than a preview grid but costs nothing. Mailpit's HTML check also flags CSS and HTML features with poor client support before you open a single app.

Whichever route you take, test the plain-text part too. Some recipients see it, and so do spam filters.

Email preview text

Preview text, also called the preheader, is the line most clients show after or below the subject. If you don't set it, Postmark notes, "the preheader text is the first line of text found within your message's body." That is often "View in browser" or your logo's alt text.

Best practice is short and specific:

  • Don't repeat the subject line. The preview should add the next most useful fact.
  • Front-load it. Postmark suggests 50–100 characters. Clients truncate at different points, so make the first few words count.
  • Never put secrets in it. Preview text shows up in notifications and on lock screens.
  • Pad it so body text doesn't leak in. If the preview is short, some clients fill the remaining space with the start of the body.

Email preview text examples that pair well with their subjects:

SubjectPreview text
Your order #1042 has shippedTrack your package and see the delivery estimate.
Confirm your email for AcmeOne click to finish setting up your account.
Your invoice for October$48.00 paid with Visa ending 4242. PDF attached.
Your trial ends FridayKeep your projects by choosing a plan. It takes a minute.
New sign-in from Chrome on WindowsIf this was you, no action needed. If not, reset your password.

To add preview text that stays hidden in the body, put a hidden element first in the <body>:

<div style="display:none;font-size:1px;line-height:1px;max-height:0;max-width:0;opacity:0;overflow:hidden;mso-hide:all;">
  Track your package and see the delivery estimate.
</div>

The full technique, including padding characters and plain-text handling, is in adding preheader text to your messages and the preheader glossary entry. Then check it in a real inbox list view. That is the only place where truncation shows.

Spam checks and inbox placement

Rendering correctly is not the same as being delivered. Check in this order:

  1. Authentication. Open the received message's original source (Gmail: Show original) and confirm SPF, DKIM and DMARC all pass, and that the DKIM d= domain aligns with your From domain. Check your published policy with the DMARC checker.
  2. Content score. Run the message through SpamAssassin or Rspamd (built into Mailpit and Mailtrap) to catch obvious triggers such as broken HTML, image-only bodies and URL shorteners. What a score means is explained in email spam score.
  3. Seed list testing. Send to a set of seed mailboxes across providers and record which folder each message lands in. It is a sample, not a verdict. Real placement depends on your recipients' engagement, which seeds do not have. See seed tests, what inbox placement tests can and can't tell you, and the free deliverability test.
  4. Ongoing signals. Google's sender guidelines ask senders to keep spam rates in Postmaster Tools "below 0.10%" and never reach 0.30%. That is a production metric that no pre-send test can show.

Link and tracking checks

Every link in a template is a test case:

  • Each URL resolves over HTTPS without a redirect chain to a login wall. Mailpit's link check and Litmus's pre-send check automate this.
  • Token links (password reset, magic link, email verification) work once, and fail politely the second time. See magic link and OTP emails for why link scanners make this harder.
  • Click tracking rewrites links through a tracking domain. Confirm that the tracking domain resolves, has a valid certificate, and redirects to the right place with UTM parameters intact.
  • Marketing mail has a working unsubscribe link and a List-Unsubscribe header (how to include one).
  • Open tracking is directional only. Apple Mail Privacy Protection loads images automatically, so a test "open" proves little.

Automated end-to-end email tests

Manual checks catch design problems. Automated tests catch regressions. A practical split:

  • Unit: render each template with fixture data and assert on subject, recipients and key strings. Make the test fail on any unresolved {{variable}}.
  • Integration: run the app against Mailpit in CI and assert through its REST API that exactly one message went to the right address.
  • End to end: drive the browser through signup, fetch the email, extract the link, click it, and assert the outcome.

A Playwright test against Mailpit's API (/api/v1/search and /api/v1/message/{ID}):

import { test, expect } from "@playwright/test";

const MAILPIT = process.env.MAILPIT_URL ?? "http://localhost:8025";

async function waitForEmail(to: string, timeoutMs = 15_000) {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    const query = encodeURIComponent(`to:"${to}"`);
    const { messages } = await (await fetch(`${MAILPIT}/api/v1/search?query=${query}`)).json();
    if (messages.length > 0) {
      return (await fetch(`${MAILPIT}/api/v1/message/${messages[0].ID}`)).json();
    }
    await new Promise((resolve) => setTimeout(resolve, 500));
  }
  throw new Error(`No email to ${to} within ${timeoutMs} ms`);
}

test("signup sends a working verification link", async ({ page }) => {
  const email = `signup-${Date.now()}@e2e.test`; // fine here: Mailpit captures everything

  await page.goto("/signup");
  await page.getByLabel("Email").fill(email);
  await page.getByRole("button", { name: "Create account" }).click();
  await expect(page.getByText("Check your inbox")).toBeVisible();

  const message = await waitForEmail(email);
  expect(message.Subject).toContain("Confirm your email");

  const link = message.Text.match(/https?:\/\/\S+\/verify\?token=\S+/)?.[0];
  expect(link).toBeTruthy();

  await page.goto(link!);
  await expect(page.getByText("Email confirmed")).toBeVisible();
});

Use a unique address per test so parallel runs don't read each other's mail, and poll with a deadline instead of fixed sleeps. For a longer treatment, including staging setups with real inboxes, see end-to-end testing with Playwright.

Testing email in production safely

Some bugs only exist in production: a DNS change that broke DKIM, an expired API key in one region, a queue worker that stopped. Test there carefully:

  • Canary sends. On a schedule, send a real transactional email to a mailbox you own and alert if it doesn't arrive within a few minutes. This is the only test that proves the whole path works right now.
  • Test-send endpoints instead of real lists. Send a campaign or template to one internal address before a launch. Volanea's POST /v1/templates/{id}/test-send and POST /v1/campaigns/{id}/test-send send to one address and prefix the subject with "[Test]."
  • Separate keys per environment. Staging should run on a test key or a separate project, so a misconfigured job cannot email customers.
  • Watch the events, not just the API response. A 200 means the provider accepted the request. Delivery webhooks tell you whether it was delivered, bounced or complained about.
  • Never test bounces on random addresses. To exercise error handling, use a capture tool's error simulation (Mailpit's chaos mode) or see how to test SMTP errors.

If a production send fails outright, start with why emails are not sending.

How Volanea fits into your email testing

Volanea is a developer-first email API and SMTP service built on Amazon SES. For testing, the relevant parts are sk_test_ keys that render and log without delivering, template and campaign test-sends, idempotency keys so retried test runs don't duplicate sends, and webhooks for delivery, bounce and complaint events. You can point an E2E suite at the same API your production code uses.

FAQ

What is email testing?

Email testing is checking a message before and after it ships: that it is triggered correctly, renders in real clients, has working links and tracking, passes authentication, and reaches the inbox. It combines automated tests, capture tools, client previews and deliverability checks.

How do I send a test email?

Trigger the email from your app, or use your provider's test-send feature, and send it only to mailboxes you own on major providers. Use your production From domain and templates so the test reflects what customers will receive.

What are the best free email testing tools?

For development, Mailpit (open source, self-hosted) captures mail and adds HTML, link and spam checks. Framework test transports in Rails, Django and Nodemailer cover unit tests. For free client checks, send to your own Gmail, Outlook and Apple Mail accounts.

What is email preview text?

It is the snippet that inbox lists show next to or under the subject line. If you don't set it, clients use the first text in the body. Write roughly 50–100 characters that add to the subject instead of repeating it.

How do I test email delivery?

Confirm SPF, DKIM and DMARC pass in the received headers, run a spam-content check, send to a seed list across providers, and in production run scheduled canary sends to an owned mailbox and watch delivery and bounce webhooks.

Is MailHog still maintained?

Its last release, v1.0.1, was in August 2020, and Mailpit's README describes MailHog as no longer maintained. Mailpit uses the same default ports (1025 for SMTP, 8025 for the UI), so switching is usually a one-line change.

Run your test sends on the free plan: create a Volanea account, 1,000 emails a month with no card, or see pricing.