
最近折腾 Supabase Auth 的 E2E 测试,踩了一个很经典的坑:邮件验证。
Supabase 的注册流程会发送一封验证邮件,测试需要点击邮件里的链接或填写 OTP(一次性密码)。但问题是——邮件发出去了,你的测试脚本根本够不着它。
官方的建议是用 InBucket——Supabase 本地开发的邮件拦截工具。但这玩意儿依赖 Docker,得在 CI 流水线里跑一个完整的 Supabase 本地实例,GitHub Actions 里还得配置 Docker 服务块,CI 构建时间蹭蹭往上涨。
其实有更简单的方案。
InBucket / 本地 Supabase: InBucket 在本地开发挺好用,supabase start 就带上了,邮件抓得飞快。但它需要 Docker、需要跑一整套 Supabase 本地栈、而且测试的不是你真实的 Supabase 项目。在 CI 里,这意味着 Docker 服务块、更慢的构建、以及一个可能和线上环境不一致的本地实例。
Mock Supabase: 你根本没在测真实的流程。测试全绿,但线上认证可能早就挂了。
数据库触发器设置可预测的 OTP: 思路巧妙,但增加了复杂度,还需要在 CI 里直接访问数据库。
共享 Gmail 收件箱: 并行测试跑起来到处都是竞态条件。
ZeroDrop 提供的是云端真实临时邮箱,在 Cloudflare 边缘接收邮件。不需要 Docker、不需要本地 Supabase、不存在共享收件箱的冲突问题。
你向真实的 Supabase 项目发起真实的注册请求,只是邮箱地址用 ZeroDrop 的临时收件箱。验证邮件不到一秒就到达。你的测试直接读取 email.otp 或 email.magicLink——自动提取,不需要正则匹配。
你的应用 → Supabase → 发送验证邮件 → ZeroDrop 接收 → 测试读取 email.otp
安装 SDK:
npm install zerodrop-client
不需要 API Key,不需要注册账号。
Supabase 默认的注册流程用 6 位数字 OTP 做邮箱验证。下面是完整的 E2E 测试写法:
import { test, expect } from '@playwright/test';
import { ZeroDrop } from 'zerodrop-client'; const mail = new ZeroDrop(); test('Supabase signup with email verification', async ({ page }) => { // 为这次测试生成一个独立的收件箱 const inbox = mail.generateInbox(); // 打开注册页面 await page.goto('/signup'); // 用 ZeroDrop 收件箱填写注册表单 await page.fill('[name="email"]', inbox); await page.fill('[name="password"]', 'TestPassword123!'); await page.click('[type="submit"]'); // 等待 Supabase 发送验证邮件 // ZeroDrop 在边缘接收 — 不到 1 秒到达 const email = await mail.waitForLatest(inbox, { timeout: 15000 }); // OTP 自动提取 — 不需要正则 expect(email.otp).toBeTruthy(); // 在验证页面输入 OTP await page.fill('[name="otp"]', email.otp!); await page.click('[type="submit"]'); // 验证用户已完成认证 await expect(page).toHaveURL('/dashboard');
});
如果你用的是免密码的魔法链接登录:
import { test, expect } from '@playwright/test';
import { ZeroDrop } from 'zerodrop-client'; const mail = new ZeroDrop(); test('Supabase magic link login', async ({ page }) => { const inbox = mail.generateInbox(); // 请求魔法链接 await page.goto('/login'); await page.fill('[name="email"]', inbox); await page.click('button:has-text("Send magic link")'); // 接收魔法链接邮件 const email = await mail.waitForLatest(inbox, { timeout: 15000 }); // magicLink 从邮件正文中自动提取 expect(email.magicLink).toBeTruthy(); // 直接访问魔法链接 await page.goto(email.magicLink!); // 用户应该已经登录 await expect(page).toHaveURL('/dashboard');
});
test('Supabase password reset flow', async ({ page }) => { const inbox = mail.generateInbox(); // 先创建一个用户(或使用已有的测试账号) // 然后发起密码重置 await page.goto('/forgot-password'); await page.fill('[name="email"]', inbox); await page.click('button:has-text("Send reset email")'); // 接收重置邮件 const email = await mail.waitForLatest(inbox, { timeout: 15000 }); // 访问重置链接 await page.goto(email.magicLink!); // 设置新密码 await page.fill('[name="password"]', 'NewPassword123!'); await page.fill('[name="confirmPassword"]', 'NewPassword123!'); await page.click('[type="submit"]'); await expect(page).toHaveURL('/login');
});
不需要 Docker。只要把 ZeroDrop 的 GitHub Action 加进去,为每次测试生成隔离的收件箱:
name: E2E Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: npx playwright install --with-deps - run: npx playwright test env: NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.SUPABASE_URL }} NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
就这么简单。不需要 supabase start,不需要 Docker 服务块。ZeroDrop 收件箱在客户端生成——没有网络请求,没有额外配置。
因为 generateInbox() 每次调用都在本地生成一个唯一的收件箱,不走网络,并行 CI 运行天然安全:
// 每个 worker 拿自己的收件箱 — 没有共享状态,没有冲突
test.describe.configure({ mode: 'parallel' }); test('user A signup', async ({ page }) => { const inbox = mail.generateInbox(); // 属于这个测试的独立收件箱 // ...
}); test('user B signup', async ({ page }) => { const inbox = mail.generateInbox(); // 不同的收件箱,零碰撞风险 // ...
});
50 个并行 worker,50 个隔离收件箱。零竞态条件。
InBucket 适合本地开发——它集成在 Supabase 本地栈里,在本地跑很顺手。ZeroDrop 适合 CI 流水线、测试真实的 Supabase 项目。
| InBucket | ZeroDrop | |
|---|---|---|
| 最佳场景 | 本地开发 | CI 流水线 |
| 需要 Docker | ✓ | ✗ |
| 测试真实 Supabase 项目 | ✗ | ✓ |
| OTP 自动提取 | ✗ | ✓ |
| 魔法链接自动提取 | ✗ | ✓ |
| 并行安全 | ✗ | ✓ |
| CI 配置复杂度 | 高 | 无 |
测试 Supabase Auth 流程的 E2E 场景,不一定非要跑一整套本地栈。ZeroDrop 让你对真实的 Supabase 项目做真实的邮件投递验证,不需要 Docker、不需要 InBucket、不存在共享收件箱冲突。
email.otp 和 email.magicLink 在 Cloudflare 边缘自动提取,然后才到达你的测试套件。不需要正则,不需要 HTML 解析,没有竞态条件。
免费使用,无需注册,CI 开箱即用。