Skip to content
Engineering

如何在CI中查看邮件而无需登录邮箱

| | 2 分钟阅读
如何在CI中查看邮件而无需登录邮箱
How to See Emails in CI Without Logging Into a Mailbox

CI非常擅长运行代码,但它在一件产品可能依赖的事情上表现糟糕:邮件。如果你的测试涵盖注册、密码重置、魔法链接或入站通知,你仍需要一种方法来在CI中查看邮件,而无需(1)登录共享邮箱、(2)抓取HTML或(3)将敏感内容转储到作业日志中。

好消息是你可以像处理任何其他测试产物一样处理邮件:按运行分配、确定性等待、对结构化字段进行断言,并将结果附加到CI输出中。

为什么”登录邮箱”会破坏CI

传统邮箱(Gmail、Outlook、IMAP)是围绕长期身份的人性化用户体验。CI需要相反的:短期、隔离、机器可读的状态。

当你在管道中依赖真实邮箱时的常见失败模式:

  • 并行运行冲突:多个作业读取同一收件箱,选择错误消息,或删除彼此的状态。
  • 认证和MFA不属于CI:OAuth刷新令牌过期,MFA质询阻止运行,权限过于宽泛。
  • 可观测性是反向的:你无法轻松存储”我们收到了什么邮件?“作为构建产物。
  • 安全性变差:团队开始将OTP和魔法链接粘贴到日志中进行调试。

如果你希望邮件可测试,它们必须像事件一样可消费。

💡 停止在CI管道中与邮件斗争

跳过邮箱登录的麻烦和并行运行冲突。Mailhook创建一次性收件箱,表现得像合适的测试资源 - 通过API按运行分配并作为结构化JSON消费。

开始正确测试邮件 →查看API文档 →

在CI中查看邮件的三种实用方法

有很多邮件测试技巧,但在2026年,大多数团队会根据他们要验证的内容收敛到其中一种。

选项1:本地SMTP捕获(快速,适用于开发和某些CI)

如果你的目标是”我们的应用是否尝试发送邮件?“那么本地SMTP捕获工具是最简单的方法。

工作原理:

  • 你的应用通过SMTP发送到CI网络内的本地服务器。
  • 服务器存储消息并通过小型API/UI暴露它们。

优点:

  • 非常快
  • 无外部依赖
  • 适合单元测试和集成测试

缺点:

  • 不是真正端到端的实际入站路由测试
  • 无法捕获生产邮件基础设施中发生的可投递性和路由问题

这是开发者工作流程的良好默认选择,但对于必须像生产环境一样运行的E2E流程,它往往不够用。

选项2:一次性收件箱API(最适合E2E和验证流程)

对于需要接收真实入站邮件并提取OTP或链接的端到端测试,可编程的一次性收件箱是最可靠的模式:

  • 通过API为每次运行或每次尝试创建新收件箱。
  • 在测试流程中使用其邮箱地址。
  • 确定性地等待到达。
  • 将消息检索为结构化JSON
  • 仅断言并提取你需要的产物。

这完全避免了邮箱登录。CI只是获取数据。

Mailhook专为此模型构建:通过API创建一次性收件箱、以JSON形式传递邮件、webhook通知、轮询检索、共享域、自定义域支持、签名负载和批处理。有关规范集成详细信息,请使用已发布的合约:Mailhook llms.txt

选项3:提供商特定的事件流(有用,但通常不是收件箱形状)

一些邮件发送提供商暴露传递事件(已接受、已退回、已传递),有时还有消息预览。当你关心可投递性遥测时,这可能很有帮助,但对于”点击此魔法链接”风格的测试,这通常还不够。

在大多数组织中,团队仍需要收件箱形状的抽象(地址、消息、检索)来进行确定性验证流程。

快速决策表

CI中的目标 最佳方法 你实际断言的内容
“我们生成了正确的邮件内容” 本地SMTP捕获 主题/发件人/收件人、文本正文、模板
“用户可以完成注册验证” 一次性收件箱API 在超时内到达、提取的OTP或链接
“可投递性和提供商行为” 提供商事件 + 针对性测试 传递事件、退回、投诉信号

如果你的管道需要以用户体验方式”查看邮件”,一次性收件箱获胜。

确定性模式:每次运行一个收件箱,等待,解析JSON

关键是停止以”搜索邮箱”的方式思考。在CI中,你需要明确的合约:

  • 隔离:每次运行都有自己的收件箱。
  • 传递:你的测试工具有可靠的方式等待消息(webhook优先,轮询后备)。
  • 消费:你对稳定的JSON字段进行断言,而不是渲染的HTML。

一个简单的CI邮件测试流程图,显示五个从左到右连接的框:创建一次性收件箱、触发应用邮件、通过webhook或轮询接收、对JSON字段进行断言、提取OTP或魔法链接。

💡 在几分钟内让确定性邮件模式工作

当你可以立即开始测试时,为什么要重建收件箱隔离和JSON解析?Mailhook为你提供开箱即用的webhooks、轮询和结构化邮件数据 - 正是此模式需要的。

免费试用 →浏览用例 →

步骤1:分配一个收件箱

在测试开始时创建收件箱并保留两者:

  • 邮箱地址(你的应用使用的)
  • 收件箱句柄(你的测试工具用来获取消息的)

使用Mailhook,确切的请求和响应字段记录在llms.txt中。在代码审查中将该文件视为真实来源。

步骤2:触发邮件

运行测试中的操作,例如:

  • 注册
  • 密码重置
  • 邮件登录

为了确定性,将你控制的关联值(例如,运行ID)传递到你的应用程序中,使其最终出现在邮件中。常见位置:

  • 验证链接内的查询参数
  • 你的邮件程序添加的自定义头
  • 邮件文本中的引用令牌

步骤3:等待到达(在CI内轮询是最简单的)

当你已经有公共端点时,Webhooks很棒。在CI中,如果你实现超时和退避,轮询通常更容易且仍然确定性。

提供商无关的轮询伪代码:

async function waitForEmail({ inboxId, matcher, timeoutMs }) {
  const started = Date.now();
  let delay = 250;

  while (Date.now() - started < timeoutMs) {
    const messages = await listMessages(inboxId); // 提供商API调用

    const match = messages.find(matcher);
    if (match) return match;

    await sleep(delay);
    delay = Math.min(delay * 1.5, 2000);
  }

  throw new Error(`在收件箱 ${inboxId} 中等待邮件超时`);
}

重要部分是匹配器:使其足够窄,以便并行运行不能匹配彼此的邮件。

步骤4:对JSON进行断言,仅提取你需要的内容

一旦你检索到结构化的JSON表示,优先选择如下断言:

  • 发件人域正确
  • 收件人匹配预期地址
  • 主题包含预期意图
  • 文本正文包含OTP模式

避免脆弱行为:

  • 抓取HTML标记
  • 依赖精确格式或CSS
  • 当你只需要OTP时让LLM”阅读整个邮件”

示例提取(OTP):

import re

def extract_otp(text: str) -> str:
    m = re.search(r"\b(\d{6})\b", text)
    if not m:
        raise ValueError("未找到OTP")
    return m.group(1)

步骤5:将邮件JSON作为CI产物附加(而不是打印它)

为了在不泄露秘密的情况下调试失败:

  • 将接收到的消息JSON写入文件
  • 将其作为产物上传
  • 在适当的地方编辑或省略敏感字段

在GitHub Actions中,产物上传很直接:

- name: 保存入站邮件JSON
  run: node scripts/save-email.js > email.json

- name: 上传邮件产物
  uses: actions/upload-artifact@v4
  with:
    name: inbound-email
    path: email.json

这是”手动邮箱调试”和”CI级邮件可见性”之间最大的实际差异。你得到一个与运行绑定的持久产物。

CI中的Webhooks:何时有意义

Webhooks在以下情况下是理想的:

  • 你运行具有稳定公共URL的长期测试环境
  • 你需要最低延迟
  • 你已经操作小型接收器服务

如果你确实使用webhooks,将webhook请求视为不受信任的输入通道:

  • 验证签名
  • 强制时间戳容忍度
  • 去重传递(会发生重试)

Mailhook支持签名负载,这是此处正确的原语。再次,确切的签名和头格式在Mailhook llms.txt中描述。

“查看邮件”的CI可靠性检查清单

这些是消除大多数不稳定性的护栏:

每次运行使用唯一收件箱(或每次尝试)

如果你共享收件箱,你最终会匹配错误的消息。隔离比调试更便宜。

优先选择确定性等待而不是固定睡眠

用”等到匹配的邮件到达或超时”替换sleep(10)。这提高了速度和稳定性。

在正确级别去重

由于重试,邮件管道可以合法地传递重复项。你的工具应该通过以下方式保持健壮:

  • 选择最新的匹配消息
  • 跟踪已见过的消息ID
  • 使消费幂等

将邮件内容视为恶意输入

即使在测试环境中,邮件也可能包含:

  • 意外的HTML
  • 跟踪链接
  • 附件

如果涉及代理,约束它可以对邮件做什么。提取最小产物(OTP或URL),验证它,然后继续。

保持秘密不在日志中

如果你必须记录某些内容,记录标识符:

  • 运行ID
  • 收件箱ID
  • 消息ID

然后将完整消息内容存储在具有适当访问控制的产物中。

最小的”在CI中查看邮件”实施计划

如果你从不稳定的共享邮箱设置开始,按此顺序迁移:

阶段1:停止登录邮箱

  • 用API检索替换邮箱UI步骤。
  • 将接收到的消息存储为产物。

阶段2:隔离

  • 每次运行创建一个收件箱(或每次尝试用于验证流程)。
  • 在邮件内容中添加关联令牌。

阶段3:使其安全和可扩展

  • 如果使用webhooks,添加webhook签名验证。
  • 添加超时、去重和清晰的错误消息。
  • 如果需要允许列表或更严格的控制,考虑自定义域。

Mailhook支持即时共享域自定义域支持,因此你可以快速开始并稍后加强控制。

Mailhook的适用场景

如果你的目标是在CI中查看邮件而永远不登录邮箱,你需要一个表现得像测试资源的收件箱:

  • 通过API按需分配
  • 作为结构化JSON消费
  • 通过webhooks传递或通过轮询获取
  • 安全地并行运行

这是Mailhook设计的核心工作流程。使用规范集成参考来实现确切的调用和负载格式:Mailhook llms.txt。你也可以从Mailhook的产品概述开始。

ci-cd email-testing automation api-integration qa-testing

相关文章