测试重试和退避逻辑
一个先失败后成功的模拟 — 因此您可以证明您的重试代码有效。
重试代码很难测试,因为真实的 API 很少会在您希望的时候失败。带有响应序列的模拟解决了这个问题:第一次调用返回 500,下一次返回 200,并重复该循环。这为您提供了一个测试套件可以依赖的确定性失败,以便您可以证明重试确实发生并且第二次尝试成功了 — 而不是假设它会这样。
您如何使调用先失败一次然后成功?
将默认响应定义为失败,并添加随后的响应序列。模拟在每次调用时按顺序遍历序列,然后重新开始,因此单个端点可以以固定顺序产生每个重试测试所需的“先失败后成功”模式,而不会有使测试不稳定的随机性。
确定性是关键所在。针对真正不稳定的端点测试重试会给您一个因与您代码无关的原因而通过或失败的测试套件。如果您确实需要随机性(用于压力测试而不是单元测试),那么错误率才是正确的工具,而不是序列。
您如何验证退避机制而不仅仅是重试次数?
计算尝试次数证明了您进行了重试;这并不能证明您进行了等待。在检查器中记录每次发送的时间戳,并检查间隔是否如您的策略所说的那样增长。所有客户端同时进行固定间隔的重试正是将短暂中断转变为持续中断的原因,这就是为什么带有抖动的指数退避是标准建议。
为模拟添加响应延迟,以测试问题的另一半:请求挂起而不是失败。有重试但没有超时的客户端会在第一次尝试时无限期地等待,并且永远不会进行重试。
哪些失败应该被实际重试?
重试一切与什么都不重试一样错误。500 或 503 值得再次尝试;400 或 422 将永远面临同样的失败,重试只会使错误成倍增加。429 有它自己的情况 — 请遵守 Retry-After 请求头,而不是应用您自己的时间表。
将这些作为单独的端点进行模拟,并在您想要的行为上进行断言:重试、不重试,或推迟到服务器说明可以重试。三个小型模拟比任何注释都能更准确地记录您的重试策略。
在某个点之后,停止重试并大声报错。在多次失败后断开的断路器既保护了您,也保护了您正在调用的服务:永远重试会将依赖项的中断转换为您自己的中断,并在发生时将其对您的警报隐藏起来。
通常会出什么错?
- 重试非幂等请求。重试的 POST 可能会扣费两次。只在重复操作无害的情况下安全地重试,或者使用幂等键。
- 没有上限。没有上限的重试会将短暂中断转变成由您自己造成的停机。
- 重试没有超时。如果第一次尝试永远没有返回,那么重试也就永远不会运行 — 要测试挂起,而不仅仅是失败。
- 同步重试。如果没有抖动,每个客户端都会以相同的节奏重试,并且正在恢复的服务将立即受到整个队列的打击。
何时使用此功能
重试代码以未经测试而臭名昭著,因为真实的 API 很少会按需失败。此模拟在第一次调用时返回 500,在第二次调用时返回 200,然后循环,这样自动化测试就可以断言您的客户端会重试并恢复。X-Mockapi-Variant 请求头会显示每个请求是由哪个步骤提供服务的。
创建模拟
curl -X POST https://quickmock.dev/api/mocks \
-H 'Content-Type: application/json' \
-d '{
"method": "GET",
"response_status": 500,
"content_type": "application/json",
"response_body": "{\"error\":\"upstream\"}",
"response_sequence": [
{ "status": 200, "body": "{\"ok\":true}" }
]
}'
调用它
curl https://quickmock.dev/m/<slug>
您将获得什么
1st call -> 500 (X-Mockapi-Variant: seq-1/2)
2nd call -> 200 (seq-2/2)
...then it cycles
常见问题解答
- 失败的顺序是确定的吗?
- 是的。响应序列是按顺序遍历的,每次调用一步,然后重复 — 因此同一个测试在每次运行时都会产生相同的状态序列。只有当您故意想要随机性时,才请改用错误率。
- 我该如何检查我的客户端重试了多少次?
- 检查器列出了带有时间戳的每次发送记录,因此请求的数量就是次数,它们之间的间隔就是退避时间。这也是您捕获客户端重试次数远多于预期的做法。
- 我能模拟请求挂起而不是错误吗?
- 可以 — 设置一个响应延迟。这是大多数重试测试都会遗漏的情况:一个没有超时的客户端会永远阻塞在第一次尝试上,并且它的重试逻辑永远不会执行。
构建您自己的模拟
创建模拟相关指南
最后更新: 2026-10-17