How to Test OTP Emails in Playwright
Email verification is one of the most common flows in web apps — and one of the hardest to test reliably. This guide shows you how to test OTP emails in Playwright using Inboxical.
The problem with email testing
Section titled “The problem with email testing”Most teams either skip email testing or resort to hacks:
- Mocking the email service — fast but doesn’t test the actual email delivery path
- Shared test inboxes — breaks in parallel test runs when multiple tests read the same inbox
- Polling with arbitrary waits —
await page.waitForTimeout(5000)hoping the email arrived
All of these lead to either false confidence or flaky tests.
A better approach
Section titled “A better approach”Inboxical gives you isolated test inboxes with a real API. Each test gets its own inbox, emails arrive in real time, and you can assert on content without polling.
Step 1: Install the SDK
Section titled “Step 1: Install the SDK”npm install @inboxical/playwrightStep 2: Set your API key
Section titled “Step 2: Set your API key”Add INBOXICAL_API_KEY to your environment or .env file:
export INBOXICAL_API_KEY=your_api_key_hereStep 3: Write your test
Section titled “Step 3: Write your test”import { test, expect } from '@playwright/test'import { inboxical } from '@inboxical/playwright'
test('OTP verification flow', async ({ page }) => { // Create an isolated inbox for this test const inbox = await inboxical.createInbox()
// Register with the test inbox email await page.goto('/register') await page.fill('[name=email]', inbox.emailAddress) await page.fill('[name=password]', 'SecurePass123!') await page.click('[type=submit]')
// Wait for the OTP email (long-polls until it arrives) const messages = await inboxical.waitForMessages(inbox.id)
// Assert email arrived expect(messages).toHaveLength(1) expect(messages[0].subject).toBe('Verify your email')
// Extract the OTP automatically const otp = messages[0].extractedOtp expect(otp).toHaveLength(6)
// Complete the verification await page.fill('[name=otp]', otp) await page.click('[type=submit]') await expect(page.locator('h1')).toContainText('Dashboard')})Why this works
Section titled “Why this works”- Isolated inbox — no interference from other tests, safe for parallel runs
- Long-polling —
waitForMessages()blocks until the email arrives, no arbitrary delays - OTP extraction — Inboxical parses 4-8 digit codes automatically, no regex in your test
- Real email path — tests the actual SMTP delivery, not a mock
Running in CI/CD
Section titled “Running in CI/CD”The same test works in GitHub Actions, GitLab CI, or any CI environment:
- name: Run Playwright tests run: npx playwright test env: INBOXICAL_API_KEY: ${{ secrets.INBOXICAL_API_KEY }}No SMTP server to spin up, no Docker containers — just an API key.
Next steps
Section titled “Next steps”- Quickstart guide — get set up in 5 minutes
- Playwright SDK reference — full API documentation
- GitHub Actions guide — CI/CD integration