Tempcase

Tempcase 测试笔记

验证码重发测试:
把每封信对回一次请求

点了两次重发,先收到的却是第二封。先别把「最新到达」当作「最新签发」:把请求、发送事件与收件记录连起来,才能判断到底是哪一步出了问题。

阅读约 12 分钟 OTP 回归

验证码邮箱能证明一封信被接收,也能让你检查正文;它不能替业务系统决定哪个码有效。本文以你有权测试的预发环境为前提,使用不承载真实资产的测试账号。示例中的编号均为说明用代号,不是线上故障记录。

2026 年的自动化回归,为什么还会把两轮认成一轮

自动化脚本常把流程写成「申请验证码 → 等待邮件 → 取最新一封 → 提交」。单次运行正常,并不代表连续重发也可靠:第一封还在队列,第二次请求已经完成;两个窗口共用一个账号,脚本却只保存了最后一次点击时间。

此时收件箱里的最新一封可能属于较早请求。只按主题、未读状态或到达顺序取信,会把投递乱序误报成验证码错误,也可能用错码碰巧通过而漏掉缺陷。测试注册邮件,首先要建立对应关系,而不是先写一个抓六位数字的正则。

先确定重发规则,不能由收件箱猜答案

在跑验证码重发测试前,向产品和后端确认一份可执行的规则:重发是复用仍有效的码,还是签发新码?新码何时撤销旧码?有效期从首次签发还是本次签发计算?规则作用于账号、用途,还是某个验证会话?

先写预期,再制造乱序

「仅最新码有效」和「有效期内多个码均可用」是不同设计。后者不应仅凭旧码通过就被判成缺陷;但两种设计都需要定义尝试次数、限流、过期与成功后的一次性消费规则。

把规则版本写进用例。若规则尚不明确,先记录待确认项,不要让自动化断言替团队做产品决定。本文后面的复现示例明确采用「新码签发成功后,旧码立即失效」。

一轮一个 case:地址只是上下文的一部分

从临时收件箱复制一个独立地址,为本轮分配代号,例如 case-r17。同一轮内的首次发送与重发保留这个地址,并记录预发版本、账号代号、验证用途及会话代号。不要每点一次重发就换地址,那会把「同账号重发」变成「新账号首次发送」。

每个请求分别记下业务请求 ID、响应结果以及发送服务生成的事件 ID。业务接口返回成功可能只说明任务入队;若异步任务尚未创建发送事件,该格先记「待关联」,之后补齐。请求被限流或拒绝时,通常不应期待一封新邮件,仍以接口契约为准。

Tempcase 的邮件记录 ID 属于收件侧,不会天然等于业务请求 ID。若收件详情没有暴露邮件头或关联字段,需要通过发信日志、受控测试标记或测试环境的观测能力补足,不能凭时间接近强行配对。

建立五列关联表,让每封信有出处

下面五列是最小关联骨架。账号、环境、规则版本和时间轴放在本轮记录头部;一个请求触发多个投递尝试时,保留多行,并注明重试关系,不要覆盖旧事件。

示意记录:R1、R2 属于同一测试地址与同一验证会话
测试轮次 请求 ID 投递事件 ID 邮件记录 ID 业务校验结果
case-r17 / R1 req-a evt-a mail-a,后到 旧码 A:预期拒绝;实际接受
case-r17 / R2 req-b evt-b mail-b,先到 新码 B:在独立复现中校验

这里的「投递事件」还应注明阶段:入队、提交发信服务、远端接受或退信。它不是笼统的「已送达」。邮件记录 ID 可以作为定位线索,但不能替代业务验证接口的结果。

连续重发、双窗口、队列延迟,分别证明什么

连续重发:检查规则与限流

在允许的重发间隔外发送 R1、R2,分别记录响应;再单独测试冷却期内的点击。核对被拒绝的请求是否真的没有新任务,以及重发后有效期是否符合约定。不要靠高频请求制造压力,也不要绕过限流。

双窗口:检查状态作用域

在两个窗口使用同一测试账号,按明确顺序触发请求,记录各自会话代号。观察撤销规则是否跨窗口生效。一个窗口还显示旧提示,不等于服务端仍接受旧码;反过来,页面已更新也不能证明服务端完成了撤销。

队列延迟:检查乱序下的业务结果

只在获授权的测试环境,通过可控队列或故障注入延迟 R1,让 R2 先投递。记录注入位置和延迟范围。这组测试证明系统能否在乱序下遵守验证码规则,不证明真实网络的投递时延,也不应修改生产队列。

邮件顺序不等于请求顺序

请求顺序、签发顺序、任务执行顺序和接收顺序可能不同。邮件的 Date 头也由发送侧生成,不能直接当成收件时间。识别验证码乱序到达,应优先使用可追溯的事件关联,而非只看最新一封。

在自己控制的模板中,可放入不含秘密的测试关联代号,或在日志侧记录请求 ID 与邮件 Message-ID 的映射。验证码本身不能充当关联键:系统可能复用同一个码,不同请求也可能出现相同数字。没有关联证据时,结论应写「无法确认所属请求」。

旧码失效,要回业务端验证

把三件事分开断言:邮件是否进入收件箱;验证码是否被业务端接受;认证成功后是否建立了正确的会话或业务状态。收到邮件,不等于码有效;验证码接口返回成功,也不一定代表登录流程已经完成。

检查旧码时,在 R2 签发成功后提交 R1 对应的码,并记录业务错误类型与挑战状态。新码是否可用、成功后能否重复使用,建议用独立复现分支验证:先用新码成功可能消费整个挑战,使后续旧码失败,却掩盖了重发撤销没有生效的问题。

每个分支都要重新建立同样的初始条件,避免超过尝试次数。只记录「接受/拒绝」及稳定错误类别,不把完整验证码写进断言报告、截图名称或持续集成日志。

时间证据:统一格式,也保留来源

建议使用带时区的 ISO 8601,汇总时统一转为 UTC,同时保留原值与来源。例如「业务服务签发时间」「发信服务接受时间」「收件服务记录时间」「浏览器观察时间」应分开命名。显示格式相同,并不代表这些机器的时钟同步。

  • 同一进程内测耗时,用单调时钟;同时留一个可对照的墙上时间。
  • 跨服务比较先确认校时情况;没有可信偏差范围,不直接相减宣称精确延迟。
  • 轮询首次发现邮件的时间只是观察时间。页面每隔数秒检查一次,会引入观察窗口。
  • 邮件头时间只能作为旁证,不替代签发、入队或业务校验时间。

如果你只知道「某次检查没有,下一次检查出现」,就记录这个区间。对无法测准的部分诚实留白,比给出毫秒级但来源不明的结论更有用。

复现示例:第二封先到,第一封后到

下面是受控示例,不代表 Tempcase 或某个业务系统发生过该故障。预期规则是:同一挑战中,R2 新码签发成功后,R1 旧码立即失效。

  1. 建立测试挑战并发送 R1,记下 req-a → evt-a,在测试队列延迟其投递。
  2. 在允许重发后发送 R2,确认新码已签发,并记下 req-b → evt-b。
  3. 先观察到 mail-b,释放延迟后再观察到 mail-a;用关联代号确认两封信的归属。
  4. 在未成功消费挑战的状态下提交旧码 A。若被接受,记录验证响应与挑战状态,立即结束本分支。

缺陷记录摘要 · 说明用示例

标题
同一挑战重发签发新码后,旧码仍可通过校验
预期
R2 签发成功后,提交 R1 旧码应被拒绝。
实际
旧码 A 被接受;关联链与校验响应已脱敏留存。
边界
乱序是触发条件,不直接证明根因在队列;仍需核查撤销与校验逻辑。

如果只是第一封晚到,而旧码被正确拒绝,不应把「乱序」直接写成认证缺陷。它可能需要改进邮件提示或页面引导,但这与安全规则失效是两类问题。

交给同事的是脱敏证据,不是完整验证码

交接包需要让另一位同事按步骤复现,而不是让他使用你的凭证。可沿用下面的清单;账号用代号,真实地址和关联 ID 按团队数据分级处理。

一份最小可复现记录

  • 环境与构建版本、规则版本、测试轮次及账号代号。
  • 初始状态、会话作用域、请求间隔与延迟注入方式。
  • 五列关联表、带来源的时间轴、脱敏后的响应类别。
  • 预期与实际、复现次数、未覆盖的分支及已知不确定项。

完整验证码、登录 token、Cookie、认证链接以及临时邮箱访问 token 都不应出现在共享报告中。截图遮挡正文中的秘密,录屏检查地址栏和开发者工具。也不建议用普通哈希保存短数字验证码:取值空间太小,容易被枚举;使用 A、B 这样的无关代号即可。

何时换地址,何时保留同一账号

测试轮次结束、需要验证全新注册路径时,换一个地址有助于隔离残留邮件。仍在检查同一账号的重发、双窗口、限流或旧码撤销时,保留原地址与上下文,否则测试前提就变了。

Tempcase 临时地址默认 3 小时有效,可续期,单个地址最长 24 小时;台内续期每次请求追加 3 小时,并受上限约束。过期后无法恢复邮件,别把收件箱当作证据仓库。需要跨天保留账号收信入口,可用长期转发登录入口建立别名;别名只转发来信,不能代替业务日志或无限期证据留存。

下一轮:先把对应关系跑通

第一次实施时,不必同时覆盖所有异常。先跑一轮可关联的正常发送,再加一次重发,最后引入可控乱序。若连邮件是否发出都无法确认,先按验证码到达排查核对发送与接收证据;到信后要检查正文布局,再看邮件模板预览工作台。

一个新 case,一条完整证据链。

先准备独立地址,再触发你有权测试的注册流程。把收到的信对回请求,把有效性结论留给业务端。