How to Automate Email Verification & OTP Workflows in Playwright and Pytest

End-to-end testing for authentication flows often fails at a single choke point: email delivery.

Whether verifying user signups, testing password resets, or checking multi-factor one-time passwords (OTP), relying on real email accounts (like a shared Gmail or Outlook inbox) introduces serious bottlenecks:

  1. Rate Limits & IP Blocks: Major email providers throttle incoming connections and trigger CAPTCHAs during concurrent CI runs.
  2. State Pollution: Shared mailboxes accumulate thousands of messages, making subject-line matching slow and error-prone.
  3. Flaky Race Conditions: Transactional mail delivery varies from 2 seconds to 3 minutes depending on outbound queues and greylisting.
  4. Cleanup Overhead: Purging test accounts after test execution adds maintenance burden.

Using ephemeral inboxes with programmatic lifespans solves this problem. Instead of persistent inboxes, tests generate throwaway addresses that receive messages in volatile memory and automatically self-destruct.

Below is an end-to-end guide showing how to integrate disposable inboxes into automated test suites across Playwright (Node.js) and Pytest (Python).

Selecting the Right Inbox Lifespan for Your Test Suite

Not all test runs have the same execution profile. Choosing the wrong mailbox duration can cause premature expiration or unnecessary resource waste:

Test Profile Recommended Lifespan Typical Use Case Live Tool Reference
Fast CI Unit / API 5 Minutes Single OTP extraction, rapid smoke tests 5-Minute Disposable Mail
Standard Auth E2E 10 Minutes Basic signup, activation link clicking 10-Minute Ephemeral Mail
Queued Webhooks 15 Minutes Third-party SMTP queues (SES/SendGrid delay) 15-Minute Mail Buffer
Multi-Stage Onboarding 20 Minutes Multi-tenant setup, team invites, KYC 20-Minute Mailbox
Staging & Integration 25 Minutes End-to-end purchasing, checkout validation 25-Minute Disposable Mail
Manual QA & Bug Triage 30 Minutes Cross-browser debugging, exploratory testing 30-Minute Temporary Mail

Implementation 1: Playwright (Node.js) with Dynamic OTP Extraction

Here is a complete Playwright test suite using the zero-dependency client tempmail10min-client and regex extractor tempmail-otp-extractor.

1. Install Dependencies

npm install -D @playwright/test npm install tempmail10min-client tempmail-otp-extractor 

2. Playwright Test Script (auth-signup.spec.js)

const { test, expect } = require('@playwright/test'); const TempMail10min = require('tempmail10min-client'); const { extractOtp, extractMagicLink } = require('tempmail-otp-extractor'); test.describe('Automated User Registration & OTP Verification', () => { let mailClient; let testEmail; test.beforeEach(async () => { // 1. Initialize a 5-minute inbox for fast smoke testing // For slower third-party delivery pipelines, bump duration to 15: // https://temp-mail10min.online/15-minute-mail mailClient = new TempMail10min({ duration: 5 }); const mailbox = await mailClient.createMailbox(); testEmail = mailbox.email; }); test('signs up and verifies 6-digit OTP code', async ({ page }) => { // Navigate to registration page await page.goto('https://app.example.com/register'); // Fill registration credentials await page.fill('input[name="name"]', 'QA Test Runner'); await page.fill('input[name="email"]', testEmail); await page.fill('input[name="password"]', 'SecureP@ssw0rd2026!'); await page.click('button[type="submit"]'); // Expect OTP input prompt await expect(page.locator('input[name="otp"]')).toBeVisible(); // 2. Poll the ephemeral inbox for incoming verification email const message = await mailClient.waitForMessage({ timeoutMs: 45000, pollIntervalMs: 3000 }); expect(message).toBeDefined(); expect(message.subject).toContain('Verification Code'); // 3. Extract 6-digit OTP using regex heuristic parser const otpCode = extractOtp(message.body_text || message.body_html); expect(otpCode).toMatch(/^\d{6}$/); // 4. Submit OTP into application await page.fill('input[name="otp"]', otpCode); await page.click('button#verify-btn'); // Verify successful onboarding redirect await expect(page).toHaveURL(/.*dashboard/); await expect(page.locator('h1')).toContainText('Welcome to Dashboard'); }); test('verifies email via activation magic link', async ({ page }) => { // Use 15-minute lifespan for multi-stage link checks // Interactive tool: https://temp-mail10min.online/15-minute-mail await page.goto('https://app.example.com/signup'); await page.fill('input[name="email"]', testEmail); await page.click('button#request-link'); const message = await mailClient.waitForMessage({ timeoutMs: 30000 }); const activationLink = extractMagicLink(message.body_html); expect(activationLink).toBeTruthy(); // Navigate directly to the tokenized activation URL await page.goto(activationLink); await expect(page.locator('.status-badge')).toContainText('Account Confirmed'); }); }); 

Implementation 2: Pytest & Python Automation

If your test suite uses Python, you can use the official tempmail10min library:

1. Install Package

pip install pytest pytest-playwright tempmail10min 

2. Pytest Test Script (test_auth_flow.py)

import re import pytest from playwright.sync_api import Page, expect from tempmail10min import TempMail10min def test_registration_with_temporary_email(page: Page): # Initialize a 10-minute temporary inbox  # Webmail equivalent: https://temp-mail10min.online/  client = TempMail10min(duration=10) mailbox = client.create_mailbox() temp_email = mailbox["email"] # Open target registration form  page.goto("https://staging.example.com/signup") page.fill("#email-field", temp_email) page.fill("#password-field", "TestSecure123!") page.click("button#register") # Wait for incoming OTP via client polling helper  email_data = client.wait_for_message(timeout=60, poll_interval=3) assert email_data is not None, "Timed out waiting for verification email" # Regex parse 6-digit code  body = email_data.get("body_text", "") match = re.search(r"\b\d{6}\b", body) assert match is not None, f"No 6-digit OTP found in body: {body}" otp_code = match.group(0) # Enter OTP into verification UI  page.fill("#otp-input", otp_code) page.click("button#submit-otp") expect(page.locator(".welcome-header")).to_contain_text("Dashboard") 

Handling Delivery Delays & Greylisting in CI/CD

When testing against staging environments, transactional mail deliverability behaves differently than production. Two primary issues frequently appear:

1. Greylisting Delays (SMTP Error 451)

Mail transfer agents (MTAs) often reject initial delivery attempts from unrecognized sending IPs with a temporary 4xx error. Legitimate mail servers retry within 1 to 5 minutes.

2. Complex Multi-Step Onboarding

Enterprise applications frequently require team confirmation: User A registers, invites User B, and User B must accept before the test completes.

Defensive Coding: Filtering Disposable Inboxes in Production

While disposable inboxes are essential for automated QA, SaaS applications often need to protect promotional credits and free trials from abuse.

If you are building authentication defenses, pair your verification flow with a normalization and detection engine:

const { isDisposableEmail, normalizeEmail } = require('tempmail10min-validator'); function validateRegistration(emailInput) { // 1. Strip subaddressing (+tag) and Gmail dot tricks const cleanEmail = normalizeEmail(emailInput); // 2. Check domain against disposable provider lists if (isDisposableEmail(cleanEmail)) { return { allowed: false, reason: 'Disposable email addresses are not permitted for trial accounts.' }; } return { allowed: true, email: cleanEmail }; } 

For performance benchmarks comparing delivery speeds across disposable mail providers, refer to our 8 Best Disposable Temp Mail Services Tested.

Summary & Best Practices

  1. Keep durations minimal for smoke tests: Use 5-Minute Mail to maintain fast CI job completion.
  2. Buffer for asynchronous queues: Scale to 15-Minute Mail when testing SendGrid or SES queues.
  3. Use regex parsers for OTPs: Never hardcode message body indexes; parse using keyword proximity.
  4. Clean up memory automatically: Rely on volatile RAM-based deletion to keep test databases sanitized.

Need an interactive disposable inbox right now? Launch a zero-signup mailbox at TempMail10min.