测试面试:重复提交,别只盯着按钮
- 时间
- 2026-10
- 来源
- 牛客网
《核心观点》
- 面试问「重复提交订单怎么测」,只答「连续点击几次」是不够的,关键是要把结果核对清楚。
- 先和需求确认系统靠什么识别「同一次下单」:同一用户再次购买,不一定算重复提交。
- 让重复真的发生:除了连点提交,还要用同一个下单标识,把同一笔业务请求发两遍。
- 按钮置灰只能挡住一部分操作路径,不能替代服务端的幂等校验。
- 更接近真实故障的两个场景是「同一请求同时到达」和「第一次响应超时后重试」,都要覆盖。
- 看返回,更要看实际记录:逐次记下请求与返回的订单号,并核对订单条数。
- 两次都返回成功,也可能只落了一笔订单;第二次报错,也不代表后台没有重复写入。
- 除了订单本身,还要按这条下单流程的设计检查关联变化,比如库存预占、优惠券占用。
- 超时场景先查第一笔的状态:应能查到或返回已有订单,而不是再创建一笔;「首次确实失败后允许重试」要与防重场景分开写清预期。
- 可以照这个骨架口述:先确认系统怎么识别同一次下单 → 覆盖连点、重复请求、同时请求、超时重试 → 用订单号核对实际创建数量 → 验证库存等关联记录 → 补一个首次失败后重试的场景。
《参考解析》
先把「什么叫重复」定义清楚:防重的前提是有幂等键。常见做法是客户端在下单前先取一个全局唯一的请求号(或直接用业务键,比如用户 + 商品 + 活动 + 时间窗),服务端拿它做唯一约束或写去重表。测试时必须先问清这个键是什么、由谁生成、重复提交时是同一个还是新的——同一个键才是「同一次下单」,键不同但用户主动再买一次属于正常复购,预期结果完全不同。这一步没对齐,后面的用例全是无效用例。
前端拦截为什么不能算验证:按钮置灰、提交后禁用、前端节流,这些都只是给人用的体验优化,绕过成本几乎为零——直接调接口、断网重放、连点两次、多端同时操作都能穿过去。所以防重必须落在服务端,且最终要落在存储层(唯一索引、去重表、状态机单向流转)才有强保证。测试报告里要明确写出「前端置灰仅覆盖 A 路径,服务端幂等由 B 用例覆盖」,而不是把前者当结论。
怎么核对「实际记录」:接口返回只是表象。两次请求都返回成功,可能后台只有一笔订单(幂等返回了已有订单);第二次返回报错,也可能是唯一索引拦下的重复写入,此时第一笔是好的;但如果是部分成功——主单落库、明细或库存没写——就会出现「订单在、库存没扣」这类脏数据。因此要按订单号核对订单条数,再顺着这条下单流程检查关联表的实际变化(库存预占、优惠券/积分占用、支付单),必要时直接查库或调后台接口比对。
超时重试的两种语义要分开写:第一种是「第一次其实成功了,只是响应没回来」,此时系统应识别出已有订单并把结果返回给用户,不能新建;第二种是「第一次确实失败」,此时应允许用户重试并正常落单,防重逻辑不能把正常重试也挡住。两者的输入看起来一样(用户又点了一次提交),预期却相反,所以用例里要把前置状态写清——这正是面试官追问「你怎么区分这两种情况」的落点。
并发同时到达与边界组合:真正难防的是两个请求同时打到不同实例、各自查了一遍「有没有重复」都没查到。这时只能靠数据库唯一索引或分布式锁/去重表在写入层兜底,测试可以用并发工具同时发同一请求号,观察是否只落一笔、另一个请求是报错还是返回已有订单。边界上还可以补:同一用户换端重复提交、请求号被复用、请求号缺失或超长、以及库存只剩一件时的并发预占。
面试口述要落在方法而不是结论:这道题的评分点不是「我知道要幂等」,而是你有没有一套可执行的验证路径。稳妥的讲法是:先确认识别同一次下单的机制 → 覆盖连点、程序重发、并发到达、超时重试四类触发方式 → 接口返回与实际记录双向核对(订单号 + 条数 + 关联表)→ 把「首次失败后重试」单独列一个用例验证防重不误伤。另外别把练习题包装成做过的项目:面试官顺着追问「请求号谁生成的、去重表怎么清理、并发压到多少出现异常」就会露底,真跑过并留下输入、预期、实际结果和核对记录,才有材料接住追问。