How to Test Password Reset Emails
A broken signup frustrates a user; a broken password reset either locks them out for good or — much worse — lets someone else in. It deserves a real automated test, and the mailbox part is easier than you think.
The flow, split into testable halves
Password reset has an email half and a token half, and teams usually test neither. The full cycle: request a reset for address A → a mail with a tokenized link arrives at A (and only A) → following the link yields a set-new-password form → the new password logs in → the old one does not → the link cannot be reused. Every one of those clauses is an assertion.
Setting up the mailbox
Use a disposable Tempx inbox as address A: any name at tempx.uk works with zero setup, mail arrives via our MX, and the inbox API returns the message with a 140-character text preview. Reset mails usually lead with the link, so the preview is enough to extract it:
ADDR="reset-91b7c@tempx.uk"
await requestReset(ADDR);
const msg = await waitForMessage(ADDR, {
match: (m) => /reset your password/i.test(m.subject),
});
expect(msg.from).toContain("noreply@example.com");
const link = (msg.preview.match(/https:\/\/example\.com\/reset\?t=[A-Za-z0-9_-]+/) || [])[0];
expect(link, "reset link in mail").toBeTruthy();
Assertions that actually protect users
- The link goes to the requested address only. If you also control the app, confirm a reset request for a nonexistent address sends nothing observable — and never leaks whether an account exists.
- Single-use. Complete the reset, then follow the same link again: it must fail with a clear message, not open the form.
- Expiry. With a short-lived token, fake the clock (or lower the TTL in staging) and confirm an expired link is rejected politely.
- The switch. New password logs in; old password is rejected. This is the assertion users bet their data on.
- The wording. Reset mails that say "someone tried to access your account" are a security communication — assert the sentence survives template changes.
When the link hides in the HTML part
Some templates put the button link only in the HTML part, outside the 140-character text preview. Three ways in, cheapest first: assert on the subject plus the preview text and keep the deep check manual; read the full body with the MCP get_email tool (any MCP client, see the integration); or open the message in the Tempx app during a manual exploratory pass.
Testing the sending side too
If your staging should never be able to reset a real person's password, point its mailer at the capture-only SMTP gateway: every reset mail is stored in your inbox with the original recipient preserved in the headers, and nothing is ever delivered outside. Misconfigured staging becomes structurally incapable of emailing a real user.
Create a temporary email address
Free, instant, no sign-up — messages auto-delete.
Go to your inbox