On this page7 sections

Email / SMS testing tools

Email and SMS testing tools catch the messages your application sends — verification emails, password resets, OTP codes, notifications — into a test inbox you can inspect or assert on automatically, instead of letting them reach a real person. They make a notoriously hard-to-test part of the product (the bit that leaves your system entirely) testable like any other.

// WHAT THEY ARE

When your app sends an email or SMS, it normally hands the message to an external provider and the message is gone — which makes it awkward to test. These tools intercept that step: they give you a capture inbox (a real or sandboxed mailbox) that receives the message instead, plus an API to read it back — assert the subject, extract the OTP code from the body, follow the verification link, check the SMS text. So a flow like "register → receive OTP → enter it → confirm account" becomes a fully automatable end-to-end test.

There are three tiers, by how you work. Local SMTP capture (Mailpit, the maintained successor to the now-abandoned MailHog) runs as a tiny local server that catches everything your dev app sends, with a web UI to inspect it — perfect for development and quick checks. Hosted sandboxes (Mailtrap) add team inboxes, HTML-rendering previews across clients, and spam/deliverability analysis for manual QA. CI/automation-grade services (Mailosaur, MailSlurp) are built for asserting on message content inside pipelines — per-test inboxes, content-extraction APIs, and SMS/OTP support — which is what you want when email/SMS is part of an automated suite.

// WHEN YOU NEED THEM

You need one the moment a user journey passes through a message: signup verification, password reset, magic-link or OTP login, MFA, transactional receipts, or any SMS notification. Without a capture tool you're either testing these by hand (slow, unrepeatable) or skipping them — and auth flows are exactly the flows you can't afford to leave untested. You also need them to test the content (does the OTP actually arrive and work? does the link point to the right place? does the HTML render?), not just that your code called send().

// The signals

  • OTP / magic-link / MFA login flows in your test scope
  • Signup or password-reset journeys that depend on a received email
  • Transactional emails or SMS whose content you need to verify
  • Wanting end-to-end tests that don't stop at "the email was queued"

// COMPARISON

ToolTierInterfaceBest for
MailpitLocal SMTP captureSelf-host binary + APIDev-time capture; MailHog replacement
MailHogLocal SMTP captureSelf-host (unmaintained)Legacy setups only — migrate to Mailpit
MailtrapHosted sandboxWeb UI + APIManual QA, HTML/spam previews
MailosaurCI / automationAPI-first, real inboxesAsserting email/SMS/OTP in pipelines
MailSlurpCI / automationAPI-first, disposable inboxesPer-test inboxes in automated suites

// OPEN SOURCE VS PAID

The local tier is free and open source: Mailpit is the standout — a single binary, actively maintained, with a REST API, HTML preview, spam checks, and even an SMTP-failure chaos mode, and it's a drop-in replacement for the abandoned MailHog (same ports, compatible API — new projects should just start on Mailpit; MailCatcher and Maildev are other free local options, and Ethereal offers disposable SMTP accounts from the Nodemailer project). The paid services buy you what local capture can't: real, addressable inboxes reachable end-to-end, content-extraction APIs designed for assertions, SMS/OTP support, and team features. Mailtrap (hosted sandbox, free tier then paid) is the manual-QA/preview option; Mailosaur and MailSlurp are the automation-grade paid services for asserting in CI. For learners: run Mailpit locally to see the capture model for free, and reach for a hosted service when you need real end-to-end inboxes or SMS in a pipeline.

// HOW TO CHOOSE

  1. 01Local dev or automated pipeline? Catching mail during development → Mailpit, free and instant. Asserting on messages in CI → an automation-grade service (Mailosaur, MailSlurp) with a real inbox and a content API.
  2. 02Email only, or SMS/OTP too? Local tools are email-only. If your flows include SMS, OTP, or MFA, you need a service that supports them (Mailosaur, MailSlurp).
  3. 03Manual QA or programmatic assertions? Mailtrap's UI is great for a human inspecting rendering and spam scores; the API-first services are built for tests that read and assert automatically.
  4. 04Still on MailHog? It's unmaintained since 2020 (no TLS, in-memory only). Mailpit is the drop-in replacement — same ports, minutes to migrate. Don't start anything new on MailHog.
  5. 05Real deliverability vs capture? Capture tools confirm your app sent the right message; they don't prove it reaches a real inbox. If inbox placement and reputation matter, that's a deliverability tool (SPF/DKIM/DMARC, spam scoring), a different job.

// COMMON MISTAKES

  • Testing that you called send(), not that the message is right. Mocking the send and asserting it was called proves your code fired — not that the OTP is correct, the link works, or the email renders. Capture the actual message and assert on its content.
  • Confusing "SMTP accepted" with "delivered." The server taking your message isn't the message reaching an inbox. Bounces from bad addresses, blocklisted IPs, or missing SPF/DKIM/DMARC happen after acceptance — don't treat a successful send as proof of delivery.
  • Leaving auth flows (OTP/MFA) manually tested or untested. These are the highest-stakes flows in the product and the most tedious to test by hand — exactly where a capture API pays off. Automate them.
  • Using a local capture tool for end-to-end delivery testing. Mailpit/MailHog catch what your app sends locally; they can't validate a real round-trip through a provider to a real inbox. For that you need an addressable hosted inbox.
  • Hard-coding waits for the message to arrive. Email/SMS are asynchronous; a fixed sleep is flaky. Poll the capture API for the message with a sensible timeout instead.