Mailpit is the right tool for localhost — free, fast, a single binary that catches everything your dev server sends. This page is about what happens when your tests leave localhost, because a redirected SMTP socket can't follow them there.
Mailpit works by impersonating your SMTP server: you point your app at its socket, and every message your code hands to the transport shows up in its UI. That's genuinely the right architecture for development — instant, private, free. But notice what it proves: your app called send. The message never touched DNS, never met your provider's API, never got rendered by the template pipeline your production config actually uses.
MailFixture sits on the other side of the wire. Inboxes are real addresses on real domains with real MX records — your app sends through whatever it sends through in that environment, and the test asserts on what actually arrived. That catches the bugs SMTP capture is structurally blind to: the staging config that still points at the sandbox provider, the DKIM setup that quietly broke, the signup form that rejects your test domain, the template variable that only renders wrong in the provider's pipeline.
It also means there's nothing to point anywhere: a preview deploy on Vercel, a QA environment someone else owns, a mobile build talking to production APIs — if it can send email to an address, you can test it.
extracted.otp.best, typed
Your regex on the body
Same shape you already write — create somewhere for the mail to land, trigger the flow, assert. The difference is the address is real, so this exact test runs unchanged against localhost, staging, or a preview deploy:
const mail = new MailFixture(); // reads MAILFIXTURE_API_KEY const inbox = await mail.createInbox({ ttlSeconds: 900 }); await page.fill('#email', inbox.address); // a real, routable address await page.click('text=Send code'); const otp = await inbox.waitForOtp({ timeout: 30_000 }); await page.fill('#otp', otp);
send; a real inbox proves the email arrived.