How to Test Email Sending with Playwright

The basic recipe — sign up, poll an inbox, extract a code — is one page long. This is the version that survives contact with a real suite: fixtures, parallel workers, CI, retries and debugging.

Start from the fixture, not the spec

A fixture gives every test a fresh inbox without repeating yourself. It wraps the polling helper so specs read as intent:

// fixtures.mjs
import { test as base } from "@playwright/test";
import { waitForMessage } from "./wait-for-email.mjs";

export const test = base.extend({
  inbox: async ({}, use) => {
    const address = "pw-" + Math.random().toString(36).slice(2, 8) + "@tempx.uk";
    await use({
      address,
      waitFor: (opts) => waitForMessage(address, opts),
    });
  },
});
export { expect } from "@playwright/test";

A spec becomes:

import { test, expect } from "./fixtures.mjs";

test("welcome mail arrives after signup", async ({ page, inbox }) => {
  await page.goto("/signup");
  await page.getByLabel("Email").fill(inbox.address);
  await page.getByRole("button", { name: "Create account" }).click();

  const msg = await inbox.waitFor({
    timeoutMs: 45000,
    match: (m) => /welcome/i.test(m.subject),
  });
  expect(msg.from).toContain("noreply@example.com");
});

Parallel workers are free — if addresses are unique

Playwright runs workers in parallel, and each test in the fixture gets its own random address. Because Tempx inboxes exist implicitly (mail sent to any name creates the mailbox), there is no setup API to race and no teardown. Two rules keep it hermetic: never reuse an address across tests, and never assert on all inbox contents — match the message, don't count it.

CI: no secrets, one env var

Named-inbox reads need no credentials, so the CI config is boring — which is the point:

# .github/workflows/e2e.yml (excerpt)
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: npx playwright test
  env:
    TEST_RUN_ID: ${{ github.run_id }}      # unique per run
    BASE_URL: https://staging.example.com

Timeouts and retries without flakes

When an email assertion fails at 2 a.m.

The copy-paste starter spec lives in the Playwright integration; Cypress and Selenium have their own.

Create a temporary email address

Free, instant, no sign-up — messages auto-delete.

Go to your inbox