跳转到内容

在你电脑上通过的测试,只需一处改动就能在 CI 中运行:API 密钥 从 CI 系统的 secrets 中读取。本页介绍这一点、如何使用地址让并行运行互不干扰、在 pull request 上提供每次运行的邮件报告,以及如何为云端编程智能体提供访问权限。

在应用的 Project settings → API keys 下创建密钥(参阅 API 密钥),并存为名为 PROXYMAIL_QA_API_KEY 的 secret。在 GitHub Actions 中:

.github/workflows/e2e.yml
jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 22
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test
env:
PROXYMAIL_QA_API_KEY: ${{ secrets.PROXYMAIL_QA_API_KEY }}

其他 CI 系统也一样:GitLab 中的 masked CI/CD 变量,CircleCI 中的 context 或项目变量,Jenkins credentials 中的环境变量。不要把密钥放进代码仓库或日志。

密钥只在一个项目中有效,所以请为 CI 单独建一个项目(至少单独一个密钥)。这样它的地址与你手动使用的地址分开,撤销 CI 密钥也不会影响任何人。

每次运行一个地址,配合加号标签

Section titled “每次运行一个地址,配合加号标签”

免费账号总共 500 个地址,每天最多 50 个,繁忙的流水线可能会用完;Team 套餐不限数量。你不需要为每个测试新建地址:发往 name+anything@ 的邮件会到达 name@,在带标签的地址上等待只会找到发往该标签的邮件。

所以为每次运行创建一个地址,并给每个测试一个自己的标签:

One address, a tag per test
// Once per run (Playwright's globalSetup, say): POST /v1/addresses
const base = 'k7f2x9ab12cd@mailclub.dev';
// In each test: sign up with a tagged version of it…
const address = base.replace('@', `+${process.env.GITHUB_RUN_ID}-${testInfo.workerIndex}-${Date.now()}@`);
// …then wait on that same tagged address.
// GET /v1/addresses/{address}/code only finds mail sent to that tag.

使用 Node.js 客户端 时,就是调用一次 qa.createAddress(),每个测试调用一次 qa.waitForCode(tagged, { since })。

有些应用的注册表单不接受 +;对于这些应用,请改为每个测试创建一个地址。特殊的邮件地址 列出了标签可以包含哪些字符。

传入 since,值为测试提交表单的时间,这样等待就不会拿到之前步骤或运行的邮件:

const submitted = new Date();
await page.getByRole('button', { name: 'Sign up' }).click();
// GET /v1/addresses/{address}/code?since=<submitted, ISO 8601>

每个密钥每分钟可发出 60 次请求(免费)或 600 次(Team)(限制)。一次等待无论持续多久(最长 30 秒)都只算一次请求,所以免费套餐下跑几个并行 worker 没有问题。大型测试套件请使用 Team 套餐,或每条流水线一个密钥。

在运行结束时用 DELETE /v1/addresses/{address} 删除地址,例如在 Playwright 的 globalTeardown 中。没删除的地址会在过期时自动删除。删除基础地址也会删除发往其标签的邮件。

Proxy Mail QA action 会把一个 job 的邮件测试归为一次 run,并把结果发布到 pull request 上:哪些邮件到达了、在测试提交后多久到达、验证码、每个链接指向哪里、你运行的邮件检查(在 Labs 中开启垃圾邮件检查时,还包括可能有多少封邮件会进入垃圾箱)、退订、优惠和优惠券检查、失败的测试,以及所有没到的邮件。同一份报告也会写入 job summary,项目中的每个人都可以在应用中打开这次 run,QA report for this run 会把这次 run 的地址和邮件生成一份 QA 报告,可打印或另存为 PDF。

.github/workflows/e2e.yml
jobs:
e2e:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v7
- uses: aaquib90/proxymail-qa-action@v1
with:
api-key: ${{ secrets.PROXYMAIL_QA_API_KEY }}
- run: npm ci
- run: npx playwright test
env:
PROXYMAIL_QA_API_KEY: ${{ secrets.PROXYMAIL_QA_API_KEY }}

该 action 会为其后的步骤设置 PROXYMAIL_QA_RUN。Node.js 客户端(0.5.0 或更高版本)、Python helper 以及 Detox 和 Appium helper 会读取它,所以测试中已有的调用都会计入这次 run。其他移动端 helper 用接收密钥的方式接收它:XCUITest 用环境变量,Espresso 用 instrumentation 参数,Flutter 用 --dart-define,Maestro 用 -e。其他工具请在请求头中发送该 id:Proxy-Mail-QA-Run: $PROXYMAIL_QA_RUN。

job 结束后,无论是否通过,该 action 都会结束这次 run 并发布报告,每次 push 都会更新其评论。以下情况 run 会失败:测试等待的邮件没到、不该来的邮件来了、邮件检查失败、测试失败、退订后仍收到营销邮件、同一人拿到两次优惠、不该被接受的优惠券被接受,或某条备注要求判为失败。如果希望此时即使测试通过也让 job 失败,请设置 fail-job: true。同名的 run 共用一条评论,所以请给 matrix job 各自命名:name: e2e (${{ matrix.browser }})。

报告还会把这次运行与同名的最近 10 次已完成运行进行比较,并列出变化:到达时间比平常长很多的邮件、现在收到两封而不是一封邮件的测试、首次失败或时好时坏的测试、新出现失败的邮件检查、上次通过但这次没有运行或失败的步骤(等待、计数、退订、优惠和优惠券检查),以及新的发件人或链接站点。每次运行保持相同的名称,这些比较才能生效。

来自 fork 的 pull request 拿不到 secrets,所以在这种情况下 action 会发出警告,不会启动运行。

@v1 跟随每个 1.x 版本,所以修复会自动到达,你无需改动。如果只想在自己选择时接受变更,可以改为固定某个版本的 commit,并在注释中写上版本号:uses: aaquib90/proxymail-qa-action@<commit> # v1.0.1。commit 列在标签页面上,Dependabot 会让固定的 commit 保持最新。

一次运行会保存地址、验证码(包括优惠券的折扣和结账时的返回结果)、耗时、检查结论、失败测试的名称和错误,以及每个链接的站点和路径,路径中看起来像 token 的部分会被隐藏。它从不保存邮件的正文或主题,也不保存链接的查询参数,登录 token 通常就在那里。运行记录保留 90 天。

在其他地方(另一个 CI 系统,或智能体的会话中),用 POST /v1/runs 启动运行,在 Proxy-Mail-QA-Run header 中发送它的 id,用 POST /v1/runs/{id}/finish 结束运行,再用 GET /v1/runs/{id}?format=markdown 获取报告。使用 Node.js 客户端时,对应的是 qa.startRun()、qa.finishRun() 和 qa.runReport()。参见 REST API。

报告可以说明每封邮件属于哪个测试,并列出失败的测试及其邮件的情况。

Playwright:从 proxymail-qa/playwright 导入 test,使用它的 qa fixture(一个在每次调用中都带上测试名称的客户端),并添加 reporter,它会记录失败和不稳定的测试以及总数。失败测试的那一行会写明失败的步骤或浏览器操作(例如 At “Enter the code › locator.fill(…)”):

playwright.config.ts
export default defineConfig({
reporter: [['list'], ['proxymail-qa/playwright/reporter']],
});
signup.spec.ts
import { test, expect } from 'proxymail-qa/playwright';
test('sign-up sends one code', async ({ page, qa }) => {
const { address } = await qa.createAddress();
// …sign up, wait for the code, check the email…
});

pytest:把 Python helper 的 hooks 导入 conftest.py:

conftest.py
from proxymail_qa import pytest_runtest_setup, pytest_runtest_teardown, pytest_runtest_logreport, pytest_sessionfinish

移动端测试:每个移动端 helper 都接收测试名称:Swift、Kotlin 和 Dart 中是 ProxyMailQA(test:),Maestro 脚本的 env 中是 TEST,Detox 和 Appium 中是当前的 Jest 测试(或 setTest)。

其他工具在 Proxy-Mail-QA-Test header 中发送经过百分号编码的测试名称,并把结果发送到 POST /v1/runs/{id}/tests(REST API)。两者在运行之外都不起作用,也绝不会让测试本身失败。

当等待返回空结果时,响应会在 Proxy Mail QA 能判断的范围内说明原因(hint,以及针对我们拒收的邮件的 refused)。在 CI 中,常见原因是 staging 环境的邮件服务商:处于 sandbox 或测试模式时,它只向已验证的地址发送,所以邮件根本没有发出。GET /v1/addresses/{address}/delivery 可以不等待就检查一个地址。更多内容见测试邮件收不到?

如果所有运行同时失败,或者 API 返回 5xx 错误,请先查看我们的状态页面,再修改你的测试。

通过浏览器登录 MCP server 需要人来操作,所以在云端运行的智能体改用 API 密钥,以 Bearer 令牌 的形式发送。给它们一个专门为此创建的项目的密钥。

在 action 之前的一个步骤中写入 MCP 配置,密钥取自 secret,并允许智能体需要的工具:

.github/workflows/claude.yml
- name: Proxy Mail QA for Claude
run: |
cat > /tmp/mcp.json << 'EOF'
{
"mcpServers": {
"proxymail-qa": {
"type": "http",
"url": "https://api.proxymail.qa/mcp",
"headers": { "Authorization": "Bearer ${{ secrets.PROXYMAIL_QA_API_KEY }}" }
}
}
}
EOF
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: |
--mcp-config /tmp/mcp.json
--allowedTools mcp__proxymail-qa__get_project,mcp__proxymail-qa__create_address,mcp__proxymail-qa__wait_for_code,mcp__proxymail-qa__wait_for_link,mcp__proxymail-qa__check_delivery,mcp__proxymail-qa__delete_address

在仓库的 Settings → Copilot → MCP servers 中添加:

{
"mcpServers": {
"proxymail-qa": {
"type": "http",
"url": "https://api.proxymail.qa/mcp",
"headers": { "Authorization": "Bearer $COPILOT_MCP_PROXYMAIL_QA_API_KEY" },
"tools": ["get_project", "create_address", "wait_for_code", "wait_for_link", "check_delivery", "delete_address"]
}
}
}

然后在仓库的 copilot 环境中,把密钥添加为名为 COPILOT_MCP_PROXYMAIL_QA_API_KEY 的 Agents secret:Copilot 只会把名称以 COPILOT_MCP_ 开头的 secrets 传给 MCP 服务器。Copilot 的编程智能体无法通过浏览器登录,所以需要密钥。

在 CI 任务中使用 Codex CLI(codex exec)时,把服务器添加到 ~/.codex/config.toml,并用 CI secret 设置 PROXYMAIL_QA_API_KEY:

~/.codex/config.toml
[mcp_servers.proxymail-qa]
url = "https://api.proxymail.qa/mcp"
bearer_token_env_var = "PROXYMAIL_QA_API_KEY"

Codex 的文档没有说明它的云端任务能否访问远程 MCP 服务器。如果你的不能,请让 Codex 改为调用 REST API,密钥取自环境设置。

对本页有疑问?

可以用任何语言提问。文档可以立即回答,你也可以把问题发给 Proxy Mail QA 团队,他们会用英文回复。