首页/开发/软件测试速查/竞态与重复提交用例
用例设计方法62 条

竞态与重复提交用例

竞态条件常在「检查后使用」的时间窗内出现:如余额校验通过后被并行扣减;用例设计重点是放大时间窗并观察最终一致性。

软件测试速查开发更新于 2026-10-13

竞态用例是什么

竞态条件出现在「检查后使用」(check-then-act)的时间窗内:余额校验刚刚通过,扣减前另一个请求已把余额用掉。竞态用例的设计重点不是穷举线程时序,而是放大时间窗、制造交错、观察最终一致性——余额与流水是否对平、库存是否为负、幂等键是否唯一。这类缺陷通常只在并发或不稳定网络下暴露。

基本信息

本质检查与使用之间的窗口、非原子序列、缓存与数据库不一致
触发手段并发同时发起、关键接口注入延迟、客户端重试与重发
观察点最终余额与流水对平、幂等键唯一、库存不为负
常见误判单线程手工测试通过就认为没问题,必须用并发工具压
修复方向数据库锁、唯一约束、幂等设计、乐观锁版本号

典型使用场景

  • 支付与退款:同账户并行扣款、退款与扣款交错执行
  • 库存与秒杀:同商品并行下单,检查超卖与负库存
  • 重复提交:快速双击、网络重试、客户端自动重发
  • 优惠券与积分:同一张券或同一笔积分被并发核销
  • 状态流转:订单取消与发货并发时的最终状态归属
  • 对账补偿:失败重试与补偿任务和主流程并发时的账目一致性

并发触发与核对

并发触发与核对
# 仅测试环境:用同一幂等键并发发起 20 个请求
for i in $(seq 1 20); do curl -s -X POST https://test.local/api/pay \
  -H 'Idempotency-Key: order-42-pay' -d 'amount=100' & done; wait
# 核对三件事:最终余额、流水条数、是否只生成一条扣款记录
# 放大窗口:在检查与使用之间通过代理注入延迟,提高交错概率
# 复查 SQL:SELECT balance FROM account WHERE id=1;

竞态用例设计要素

要素做法断言
放大窗口在检查与使用之间注入延迟交错概率提升且可复现
触发手段并发同时发起、重试与重发重复请求被幂等处理
关键断言余额与流水对平、幂等键唯一、库存不为负数据最终一致有据可查
失败恢复重试与对账补偿路径补偿后账目仍对平
副作用检查通知、积分、日志只产生一次无重复副作用
手工点击的并发度太低,几乎命中不了竞态窗口;把并发发起与故障注入纳入例行测试项,比指望「碰巧复现」可靠得多。
延迟注入与并发放大只能在测试环境做:生产环境注入延迟会直接伤害真实用户,还可能触发风控与限流,把测试事故放大成生产事故。

易错点

  • 只做并发压测不校验数据:请求都返回 200,但账目早已对不平,压测必须配一致性断言
  • 靠手工点击测重复提交:人手并发度太低,必须用并发工具同时发起相同业务请求
  • 幂等键随手生成:每次请求用新键等于没有幂等,同一笔业务的重复请求要复用同一个键
  • 忽略缓存层:缓存旧值与数据库交错读取会掩盖真实竞态,断言要覆盖最终数据来源
  • 修复后只验证一次:竞态修复必须回归并发场景与压测,单次手工通过不足以关闭问题

数据来源与参考

  • MIT 6.031 课程材料(Race Condition 章节)
  • RFC 9110(幂等性语义)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌