本层判断标准

把「别人的 id」换成「不存在的 id」,两次响应的状态码与整段正文是否逐字节相同?

不同就是在泄露存在性——越权请求能探到「这条记录存在」。补一条比较整段正文的断言;只比状态码的写法在这里不算通过。

接口测试与单元测试的分工

两者装的是同一条要求的两端:单元测试挡「算错了」,接口测试挡「接错了」。判据不是覆盖多少代码,是这次改动落在哪一层。

维度单元测试接口测试
测什么函数的输入与输出HTTP 语义:状态码、响应头、正文结构与字段
不测什么路由、序列化、鉴权中间件函数内部实现与分支覆盖
依赖无网络无数据库,或全部用替身真实路由 + 测试用存储;会话由测试注入
典型失误断言的是替身自己设的常量只断言状态码,不比较正文
改动后触发函数实现变了路由、请求与响应结构、权限规则变了

代价也要认:接口测试比单元测试慢、更依赖环境,不要用它覆盖每一个分支。两者都测同一个函数时,是重复,不是保险。

长什么样子

一次接口用例是「会话 + 请求 + 对照」三段:会话决定这次是谁,请求从进程内发出,对照拿两个不同的 id 比同一件事。

一次接口用例的三段

会话  注入本范围的会话,不从请求体读范围值
请求  进程内构造,不监听端口
对照  别人的 id 与不存在的 id → 两段响应逐字节相同

要认的是这个形状:越权与不存在必须是同一段响应。一处用 404、一处用 403,或文案里带着「无权访问」,形状上就泄了存在性。

助手交出来的样子

resp := GET("/items/1001")        他范围的 id,带本范围会话
assert.Equal(200, resp.Status)    只断言状态码
(不比较正文,也不与「不存在」那条对照)

认它的办法:看它有没有比两段正文。只断言状态码时,越权返回 403 也绿、返回带「无权访问」字样的 404 也绿——而这两种都在泄露存在性。这条用例跑了,等于没跑。

典型应用示例:别人的 id 与不存在的 id

场景:让助手给详情接口补测试。同一个会话,一次查不存在的 id,一次查属于其他范围的 id。

有它 一条用例把两次响应取出来,比较状态码与整段正文。两种情况必须逐字节相同——越权与不存在对外是同一件事。改动里一旦用状态码或文案区分了它们,这条立刻变红。
没有它 只断言 404。越权那条返回的是「无权访问」,状态码同样是 404,测试全绿——但接口已经能靠文案差异探测「这条记录存在」,没人会发现。

基本注意点

一个接口至少覆盖这四类;前三类管越权,最后一类管写入。判据的来源写在接口契约里,测试照着它写。

判据挡住什么漏了会怎样
没有会话时拿不到数据,正文里不含任何标题未登录也能读鉴权中间件漏了也没人知道
越权与不存在的响应逐字节相同用状态码或文案区分,泄露存在性外部可以逐条探测哪些 id 存在
本范围的响应里有自己的数据、没有其他范围的数据过滤没生效,越权放行接口看着正常,数据已经越界
同一幂等键重复提交只落一条;字段缺失返回 400 并指出字段名重复落库、校验错误变成 500重复扣款、客户端拿到 200 的错误

幂等那一条把判定落在数据上:查一次行数,不能只看第二次的状态码——只回同一个 id 却插了两行,同样算不通过。错误必须落在 4xx 或 5xx,不要返回 200 再在正文里放失败标记。

判据写在接口契约里——路径、参数、状态码、字段,它是测试与客户端共同的那一份依据,改字段要走单独评审;契约只写外部可见的部分,内部实现写进契约会让每次重构都改契约。测试在进程内构造请求,不监听端口,会话由测试注入,不从请求体读范围值——否则测试自己就把越权通道留出来了。命令挂进门禁那一条入口命令。