接口设计的可测试性

本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。

错误码不分层的接口,你只能靠 msg 断言,改个文案用例就跑失败。返回快照的字段返回了实时,数据就无法核对上。

接口设计得好不好,一个重要的判断标准是:验证它麻不麻烦,也就是可测试性。

接口设计的可测试性

决策1:URL 是名词还是动词?

RESTful 到底在解决什么?我们知道GET/POST/PUT/DELETE对应查/增/改/删,它有用的地方在于,URL 描述资源、方法描述动作,你能猜出接口长什么样。

# 不好:每个接口一个动词,URL 无规律,猜不出来
"/createOrder", "/orderCancel", "/getOrderInfo"

# 好:资源 + 动作,规律可预测
"POST   /orders"              # 创建
"POST   /orders/{id}/cancel"  # 业务动作,允许不严格 REST
"GET    /orders/{id}"         # 查询

什么时候违反?业务动作不是CRUD:取消订单、申请退款,写成POST /orders/{id}/cancel比硬套PUT /orders/{id}更友好。

测试视角:URL 有规律,你的自动化脚本就能参数化、能批量生成用例、新人接手也能快速明白。

决策2:错误码

三类错误必须分开:

最坑的是什么都返回200:

# HTTP 状态码是 200,但 body 说失败了 —— 到底算成功还是失败?
return 200, {"code": 500, "msg": "系统繁忙"}

后果:网关认为成功不重试、监控认为健康不告警、前端不知道该不该重试。你的用例也会误判,明明失败了,断言 HTTP 200 却过了。

三种错误的返回对比:

# 参数错:调用方的锅
return 400, {"code": "INVALID_PARAM", "msg": "count 必须大于 0"}

# 业务拒绝:传对了,但业务不允许 —— HTTP 仍是 200
return 200, {"code": "STOCK_NOT_ENOUGH", "msg": "库存不足"}

# 系统错:我的锅,你得重试
return 500, {"code": "INTERNAL_ERROR", "msg": "服务异常"}

测试视角:断言 code,永远别断言 msg。更重要的是:这些失败场景我怎么造?

  • 库存不足怎么造?(把库存改成 0?有没有隐藏库存?)
  • 券过期怎么造?(能改有效期吗?还是要等它自然过期?)
  • 支付失败怎么造?(有没有挡板?要不要真实小额支付?)

决策3:快照 vs 实时

判断依据:这个值变了算不算问题?变了算问题→快照(价格、商品名、地址);变了才正常→实时(库存、状态)。

有个常见的坑:应该快照的返回了实时。比如运营改价 99→89,历史订单详情页重新查商品价格→显示 89,但用户实付 99→账对不上。

{
  "order_id": "20260914001",
  "sku_name": "xxx 商品",      # 快照:下单时的名字,之后改名不影响
  "price": 99.00,              # 快照:下单时的价格,之后改价不影响
  "stock_left": 5,             # 实时:当前剩余库存
  "status": "WAIT_PAY"         # 实时:当前状态
}

测试视角:这个字段源头改了之后,该跟着变还是不变,最好一开始就跟研发确认清楚。不只是价格,收货地址(用户改了地址,历史订单不能跟着变)、商品名(下架改名后订单还得能看)、优惠券信息(券删了订单还得显示用了什么券)都要快照。

决策4:版本兼容

结论:加字段尽量兼容;加枚举值要按破坏性变更处理;删/改必须走版本。

RPC接口还要注意序列化问题,新增字段放在最后追加,如果放中间可能序列化报错。

例子:加个枚举值后影响历史功能:

# v1 订单状态:WAIT_PAY / PAID / DONE
# v2 加了:"PARTIAL_REFUNDED" 部分退款

# 老 App 的写法:不是 DONE 就显示"待处理"
if status != "DONE": show("待处理")
# 结果:部分退款的订单,老 App 一直显示"待处理" → 用户投诉

测试视角:兼容性用例要按版本号驱动——你的自动化里必须有"老版本 App + 新接口"这一组。

决策5:幂等

服务端不该猜"这两个请求是不是同一个"(靠参数比对、靠时间窗都不靠谱),得客户端明确传一个幂等标识。

# 客户端生成,全局唯一,同一次操作重试用同一个
{"request_id": "a1b2c3-xxxx", "sku_id": 1001, "count": 2}

服务端拿 request_id 做去重键,第一次处理,之后直接返回首次结果。第二次返回必须跟第一次一模一样,具体做法在第3篇介绍。

测试视角:有了幂等标识,你才能可靠地造重复请求——把同一个 request_id 发两次就是重试,换一个就是新请求。

二、汇总表

三、结尾

以后接手一个接口,再搞懂3个问题:

  1. 它的错误码,能让我一眼分清"我传错了 / 业务不允许 / 服务挂了"吗?
  2. 返回里哪些是快照、哪些是实时?
  3. 我能不能造出它的失败场景?

下一篇介绍幂等。"重复"和"并发"是两回事,大多数所谓的幂等,只防得住重复,防不住并发。