MoeMail
Back to Blog

Testing Password-Reset Email Flows

Password reset is a critical path that's easy to break and awkward to test, because it hinges on a unique link delivered by email. Here's how to automate it end to end with a disposable inbox API — part of email testing for developers and a close cousin of OTP and 2FA testing and magic-link login.

The flow

  1. Provision a fresh inbox and create a user with that address.
  2. Request a password reset for it.
  3. Read the reset email and pull out the link.
  4. Open the link, set a new password, and assert you can log in.

Example (Playwright)

const inbox = await createInbox()
await page.goto('/forgot-password')
await page.getByLabel('Email').fill(inbox.address)
await page.getByRole('button', { name: 'Send reset link' }).click()

const msg = await waitForMessage(inbox.id, 30000)
// take the first http(s) URL in the email body
const link = (msg.links && msg.links[0])
  || msg.text.split(/\s+/).find(w => w.indexOf('http') === 0)

await page.goto(link)
await page.getByLabel('New password').fill('S0me-Str0ng-Pass')
await page.getByRole('button', { name: 'Reset password' }).click()
await expect(page.getByText('Password updated')).toBeVisible()

The createInbox / waitForMessage helpers are the same ones from the Playwright tutorial; in Cypress use the custom commands instead.

Edge cases worth covering

Expired or already-used links, requesting a reset for an unknown email (should look identical to avoid user enumeration), and rate limiting. Each is a quick variant of the flow above.

Get started

Create a mailbox, grab an API key, and read the OpenAPI docs to plug the inbox calls into your suite.