菜单

自动化测试中的临时邮箱 API:创建、轮询、验证

在自动化测试里使用临时邮箱 API:通过 HTTP 创建收件箱、轮询确认邮件、提取验证码,并在 CI 中完成清理。

发布于

  • 自动化
  • 测试邮箱
  • 持续集成

确认邮件是注册流程里浏览器测试自己走不完的一环。总得有个东西持有地址、收下邮件、把验证码交回给测试。临时邮箱 API 做的就是这件事:测试套件通过 HTTP 向服务申请一个收件箱,读取到达的邮件,用例结束后丢弃它。没有浏览器,没有多人共用的信箱,也不需要手工复制粘贴。

本文讲的是这套机制里 API 的那一侧。它不是把被测应用指向本机收件端口——那是在持续集成里捕获邮件的做法,两者解决的是不同问题。本机捕获证明的是你的应用发出了什么;临时邮箱 API 证明的是邮件经过真实投递链路、真的到达了一个由测试持有的地址。

为什么要在测试里用邮箱 API?

因为另一个选择要么是真人信箱,要么什么都没有。

共用信箱是糟糕的测试夹具。多个运行读同一个信箱,上一轮的消息还在里面,地址还会积累没人要的流量。于是每条断言都得猜哪封信属于当前用例,而猜测正是偶发失败的来源。

用浏览器去抓服务商网页是第二个坏选择。它让测试依赖随时会变的页面结构、依赖会话状态,还要看管一个登录。服务商一改按钮样式,绿色套件就会因为与产品无关的原因变红。

邮箱 API 同时去掉这两个问题。地址是为用例创建的,天生是空的,读取走的是测试能直接调用的稳定接口。套件不再关心服务商长什么样,只关心契约成立:创建、接收、读取、删除。

还有一个隐私层面的理由。通过接口创建的地址不代表任何人。它不是某个人的信箱,真实邮件不会落到那里,而且它随这次运行一起被丢弃。

测试真正需要哪些端点?

邮箱 API 可以暴露几十条路由,但测试客户端只需要四类操作,而且最好按套件使用它们的方式来命名。

创建返回一个地址和一个句柄。地址是要告诉被测应用去发送的目标;句柄通常是令牌或标识符,测试在后续每次调用里用它来询问这个收件箱。套件应该把两者当成一个对象,绝不要从地址反推句柄,因为服务商完全可以让两者毫无关系。

列表返回摘要而不是正文:每封邮件一条,带标识符、发件人、主题和到达时间。这是轮询循环应该用的调用,因为它便宜,而且足以回答早期唯一重要的问题——有没有东西到达。

读取返回一封完整邮件,包含纯文本和 HTML 两部分。验证码就在这里,这个调用应该只在列表报告命中之后才发出。

清空把收件箱里的邮件删掉,或者整箱删除。测试需要它有两个原因:在多次尝试之间复位而不新建地址,以及用例结束时做清理。

有些服务还提供等待或长轮询端点,在消息到达或超时前一直挂着连接。它很方便,但客户端仍应能退回列表,因为等待调用最容易被限流。

如何轮询验证码而不引入偶发失败?

这类测试里最常见的错误是固定睡眠。写死几秒是猜:投递慢时太短,投递快时又白等,在负载高的 CI 机器上两个方向都可能错。把它换成一个循环:调用列表、检查是否命中、一旦找到就返回,并设一个上限,超时就判失败而不是把任务挂死。

匹配是问题的另一半。测试要的那封信,是发给当前用例所创建地址的那封;如果收件箱里可能有多种邮件,还要选主题里带稳定片段的那封。命中多封时取最新的,这样重试带来的重复投递不会干扰读取。绝不要无条件取第一封——在复用地址的情况下,这正是把旧验证码当成新验证码来校验的方式。

提取要有锚点。正文里可能有参考号、时间戳和价格,一个抓取第一串数字的解析器有时会抓错。先找到引出验证码的措辞,再从它附近读取验证码,匹配不到时就把原正文一起报出来。

最后,尊重重发限制。验证码流程通常只允许在短时间窗口内发几次,这个限制本身就是被测行为的一部分。测试为了拿到新验证码再去点一次按钮,最终会被拒绝,然后因为错误的原因失败。重试应当是再读一次信箱,而不是再触发一封邮件。

如何接入端到端或 CI 套件?

干净的做法是一个夹具。流程开始前,夹具创建一个收件箱并返回地址;测试用这个地址驱动应用;应用确认已经发出之后,断言读取信箱并提取验证码;用例结束时,夹具删除收件箱。

客户端要小、要可注入。用一个模块包住这四类调用,测试只依赖这个模块,而不是让原始 HTTP 散落在套件各处。这样既能给单元测试换成假实现,也能把同一套件指向别的服务商而不重写断言。

在 CI 里,凭据放在任务的密钥存储中,绝不进仓库,也绝不进日志。给每个任务或每个并行 worker 自己的收件箱,并在生成的地址上加能标识本次运行的前缀,这样一封走失的邮件可以靠观察来归属。把客户端超时设得比任务本身的超时更短,卡住的轮询就会给出清晰信息,而不是让任务被突然取消。

重试读取,而不是重跑整个流程。验证码还没到时,等一等再读;重跑注册会再产生一封邮件,也就给断言多出一个候选项。另外,别把邮箱 API 用进生产流程:它是测试基础设施,套件永远不应该通过它给真实客户发信。

围绕验证码的流程本身,而不是读取机制,见邮箱验证流程怎么测;解析这一步单独展开在端到端测试里的 OTP。

隔离与清理

每个用例一个地址,是避免大多数跨用例失败的原则。它省去了判断哪封信归谁的推理,也让新鲜度问题消失,因为这个收件箱从头到尾只收到过一个用例的流量。

清理要显式且无条件。在无论用例通过还是失败都会执行的收尾里删除收件箱,而不是只在成功路径上做。只依赖服务商的存活时间是个错误:邮件可能留得够久,久到被同一台机器上稍后的运行读到;那个存活时间只是便利,不是保证。

如果清理失败,记下来然后让套件跑完。清理错误值得知道,但它不等于产品缺陷;因为清理失败就判整轮失败,会教会团队忽略收尾阶段的报错。地址收到的一切都按测试数据处理:它只为一条断言存在,不应被导出或分享,也绝不能被当成任何人的联系方式。

限制与注意事项

邮箱 API 仍然是第三方依赖,它的限制就是你测试的限制。每分钟限流可能拒绝大规模并行运行带来的批量创建;配额会限制同时存在的地址数量;邮件可能延迟,而延迟的邮件在到达前看起来和丢失一模一样。

临时域名也普遍被封。服务商的域名可能被你在测的那个注册表单拒收,让一个本来正常的测试变成看不懂的失败。遇到这种情况,答案不是给服务商开特例,而是先弄清被测产品是不是有意拒绝临时地址,然后有意地把这个行为测出来。

诚实的总结是:邮箱 API 适合断言到达了什么;要断言你的应用发出了什么,本机收件端更快,也没有配额。成熟套件通常两者都用:大部分断言走本机捕获,只有在真实投递链路本身是被测对象时才用邮箱 API。

下一步

找出一个轮询共用信箱、专门读验证码的测试,把它换成一个从邮箱 API 创建新地址的夹具。把地址随运行一起打进日志,在收尾里删掉它,然后看看你原本容忍的偶发失败有多少直接消失了。需要人工收一封真实邮件时,临时邮箱片刻就能创建一个地址;它发出去的地址只是某次运行的脚手架,从来不是真实身份。

继续阅读

临时邮箱(一次性邮箱 / 10 分钟邮箱)相关文章