Transactional Email Testing Checklist: 40 Things People Forget
A classic I’ve caught more than once: registration works, but the password-reset email lands in spam. Or it arrives with “Hello, {{first_name}}” instead of a name. Or the “confirm your email” link expires in 15 minutes while the email takes 20 to arrive. Nobody tested it, because “it’s just an email, what could break.”
Plenty breaks — and the cost is high: an undelivered password-reset email = a locked-out account and a support ticket; an email in spam = a lost signup. Transactional emails (welcome, password reset, receipts, alerts) are part of the product, and you test them like the product.
Why email is QA’s blind spot
An email travels a long path: an event in the app → a template → rendering with variable substitution → sending via a provider (SES/SendGrid/Postmark) → the recipient’s filters → rendering in a specific client. A bug can live at any seam, and only whoever actually opens the email in a real inbox sees it — not “the send returned 200.”
Trigger and content
- The email arrives on the right event and exactly once (not on every request retry).
- The right template was used (not welcome instead of a receipt).
- Variables are filled: name, amount, date, link. Test empty/missing fields — there must be no “{{name}}”, “undefined”, “null”, or blank strings.
- Plurals and formats: “1 item / 2 items / 5 items,” amounts and dates by locale.
- Special characters and Unicode in the name/data don’t break the layout or inject HTML (escaping — otherwise XSS via email).
Deliverability (does it even arrive)
- SPF, DKIM, DMARC are set up and pass — otherwise mail goes to spam or bounces.
- Run it through spam scoring (mail-tester and the like): attachments, “spam words,” broken HTML, a missing plain-text part all hurt reputation.
- Correct From / Reply-To / Return-Path, a recognizable sender name.
- Check delivery in Gmail, Outlook/Office365, Apple Mail, and regional providers — everyone’s filters differ.
- Bounces and complaints are handled (hard bounce → don’t send again).
Links and tokens
- Links point to the right environment (not staging from a prod email — a frequent, embarrassing bug).
- The token lifetime is reasonable and aligned with delivery latency (the link mustn’t expire before the email arrives).
- One-time links (password reset, confirmation) work exactly once.
- Links don’t break on line wrapping in the client; tracking redirects don’t fall apart.
- Unsubscribe works and is mandatory for bulk mail (List-Unsubscribe header + link).
Rendering in email clients
- Subject and preheader are meaningful, not truncated, no “(no subject)”.
- There’s a plain-text version (multipart) — not only HTML.
- Dark mode: the email is legible (dark text on a dark background is the classic fail).
- Images off (the default in many clients):
altpresent, the email makes sense without images, key info isn’t inside an image. - Gmail clipping: an email heavier than ~102 KB gets clipped — keep it compact.
- Outlook (the Word engine) breaks modern CSS — test it separately; the mobile layout is responsive.
- Check rendering via caniemail/preview services, not “it opened fine in my Gmail.”
Timing, duplicates, retries
- No duplication: a send retry or a double click doesn’t send two emails (idempotency by event key).
- Latency is acceptable; critical ones (password reset, OTP) are fast.
- The queue survives a provider outage: the email isn’t lost and isn’t sent 5 times after recovery.
Localization and legal
- The email language = the user’s language/locale; RTL doesn’t break.
- For marketing — a physical sender address and unsubscribe (CAN-SPAM/GDPR).
- Personal data in the email is minimal (emails get forwarded and logged).
Checklist (quick pass)
- Arrives on the right event, exactly once, the right template.
- All variables filled; empty fields don’t produce ”{{…}}”/undefined; escaping in place.
- Plurals, amount/date formats by locale.
- SPF/DKIM/DMARC pass; spam score green.
- Delivery verified in Gmail / Outlook / Apple Mail / regional providers.
- From/Reply-To correct; bounces handled.
- Links point to the prod environment; tokens one-time and don’t expire before delivery.
- Unsubscribe works (link + List-Unsubscribe).
- Subject/preheader not truncated; a plain-text version exists.
- Legible in dark mode and with images off (alt in place).
- Not clipped in Gmail (<~102 KB); Outlook and mobile checked.
- No duplication on retries/double click; critical emails are fast.
- Correct locale; for bulk mail — sender address and consent.
In short — what to take with you
- A transactional email is part of the product, not “just an email.” Open it in a real inbox.
- Deliverability (SPF/DKIM/DMARC + spam scoring) matters more than the layout: an undelivered email = 0.
- The three most common bugs: an empty variable (“{{name}}”), a staging link from prod, an expired token.
- Test rendering in dark mode, with images off, and in Outlook — that’s where it breaks.
- Send idempotency — so a retry doesn’t turn into 5 emails.
Further reading: Can I email — support across email clients · Mail-Tester — spam-score and SPF/DKIM check · DMARC.org — on email authentication