API 测试各种边界情况

2 min read
Zekari
API测试软件工程最佳实践

边界情况总是被忽略

大多数 API 测试只覆盖「正常流程」。

用户输入正确的数据。服务器返回预期的响应。一切运行良好。

但现实世界不是这样的。用户会发送空字符串。网络会突然断开。数据库会返回异常数量的记录。

边界情况是系统的真实测试。它们暴露设计的缺陷。它们揭示假设的脆弱。

💡 Click the maximize icon to view in fullscreen

为什么边界情况重要

边界情况不是异常。它们是常态的一部分。

一个空数组不是错误。它是有效的数据结构。一个超长的字符串不是攻击。它可能只是用户的真实输入。

系统需要处理这些情况。不是因为它们罕见,而是因为它们不可避免。

当你跳过边界情况测试,你不是在节省时间。你是在积累债务。这个债务总会在生产环境中暴露。

边界情况的类型

边界情况不是无限的。它们遵循模式。

空值情况:

  • nullundefined、空字符串 ""
  • 空数组 []、空对象 {}
  • 空白字符串 " "

极端值:

  • 最小值:0-Infinity、负数
  • 最大值:Number.MAX_SAFE_INTEGER、超长字符串
  • 特殊值:NaNInfinity、科学计数法

格式边界:

  • Unicode 字符、Emoji
  • SQL 注入字符:'; DROP TABLE --
  • XSS 向量:<script>alert(1)</script>
  • 路径遍历:../../etc/passwd

分页和限制:

  • 零结果
  • 恰好一条记录
  • 刚好达到限制(如 100 条)
  • 超出限制(如 1000 条)

批量操作:

  • 空批次
  • 单个项目的批次
  • 最大允许批次大小
  • 超出批次大小限制

时间相关:

  • 过去的时间戳
  • 未来的时间戳
  • Unix epoch 零点:1970-01-01
  • 时区边界情况

超时和延迟:

  • 请求超时前一秒
  • 请求恰好超时
  • 极慢的响应(几分钟)
  • 并发请求导致的竞态条件

资源状态:

  • 资源不存在(404)
  • 资源已被删除(410 Gone)
  • 资源被锁定(423 Locked)
  • 资源冲突(409 Conflict)

权限边界:

  • 未认证用户
  • 认证但未授权
  • 过期的 token
  • 被吊销的权限

测试边界情况的思维方式

边界情况测试不是清单。它是一种思维方式。

问自己:这个函数假设了什么?

如果它假设数组不为空,测试空数组。 如果它假设字符串是 ASCII,测试 Unicode。 如果它假设网络稳定,模拟超时。

每个假设都是一个潜在的边界情况。

💡 Click the maximize icon to view in fullscreen

实践中的边界测试

好的边界测试有几个特征。

它是独立的。 每个测试只验证一个边界情况。不要在一个测试中混合多个边界。

它是可重复的。 无论运行多少次,结果都相同。边界情况测试不应依赖外部状态。

它是有意义的。 测试真实可能发生的边界,而不是理论上的极端情况。

// ❌ 不好的测试:混合多个边界
test('handles edge cases', () => {
  expect(api.create(null)).toThrow()
  expect(api.create('')).toThrow()
  expect(api.create([])).toThrow()
})

// ✅ 好的测试:每个边界独立
test('rejects null input', () => {
  expect(() => api.create(null))
    .toThrow('Input cannot be null')
})

test('rejects empty string', () => {
  expect(() => api.create(''))
    .toThrow('Input cannot be empty')
})

test('handles empty array gracefully', () => {
  const result = api.batchCreate([])
  expect(result).toEqual([])
})

边界情况揭示设计问题

边界情况测试不只是防御性编程。它是设计反馈。

当一个函数难以测试边界情况,通常说明设计有问题。

过多的 if-else 处理边界?可能需要重新思考函数职责。 边界情况需要复杂的 mock?可能耦合度太高。 边界测试代码比实现还长?可能逻辑过于复杂。

边界情况是系统的镜子。它反映设计的清晰度。

最后

边界情况测试不是为了达到 100% 覆盖率。

它是为了理解系统在什么情况下会失败。

理解失败比追求完美更重要。因为所有系统都会失败。唯一的区别是,你是否知道它会在哪里失败。

边界情况测试让失败变得可预测。可预测的失败可以被处理。可以被监控。可以被修复。

这就是边界情况测试的价值。


  • 你的 API 在什么边界情况下会崩溃?
  • 哪些边界情况是你从未考虑过的?
  • 生产环境中最常见的边界情况失败是什么?
  • 你的错误处理能区分边界情况和真正的错误吗?

参考资源

Related Posts

Articles you might also find interesting

端到端 Postback 模拟测试

2 min read

真实的测试不是模拟完美的流程,而是重现真实世界的混乱。Postback 测试的价值在于发现系统在不确定性中的表现。

测试API
Read More

文档标准是成本计算的前提

3 min read

API文档不只是写给开发者看的。它定义了系统的边界、成本结构和可维护性。统一的文档标准让隐性成本变得可见。

API文档
Read More

用 AI Agents 加速测试环境配置

3 min read

测试环境的配置是重复的琐事。环境变量、测试数据库、配置文件——这些步骤消耗时间但不产生直接价值。AI agents 改变了这个等式。

Claude Code测试
Read More

在Claude Code中写单元测试:简单高效的实践

2 min read

测试不是负担,是对话。Claude Code改变了测试的成本结构,让测试回归本质:验证行为,而非追求覆盖率。

Claude Code测试
Read More

CRUD 操作

2 min read

四个字母背后,是数据的生命周期,是权限的边界,也是系统设计的基础逻辑

系统设计软件工程
Read More

让文档跟着代码走

2 min read

文档过时是熵增的必然。对抗衰败的方法不是更频繁的手工维护,而是让文档"活"起来——跟随代码自动更新。三种文档形态,三种生命周期。

文档软件工程
Read More

错误隔离

3 min read

失败是必然的。真正的问题不是失败本身,而是失败如何蔓延。错误隔离不是为了消除失败,而是为了控制失败的范围。

系统设计可靠性工程
Read More

实现幂等性处理,忽略已处理的任务

3 min read

在代码层面识别和忽略已处理的任务,不是简单的布尔检查,而是对时序、并发和状态的深刻理解

系统设计并发控制
Read More

使用Jest或Vitest作为测试框架有什么区别?

1 min read

测试框架的选择不是功能列表的比较,而是关于工具哲学的选择。Jest代表完整性,Vitest代表原生性。

测试工程实践
Read More

缺少Jest依赖时的测试选择

4 min read

测试不一定需要框架。有时候最简单的工具已经足够。Node.js内置的assert模块和基础测试能力,往往比庞大的测试框架更清晰。

测试工程实践
Read More

幂等性检查

1 min read

在不确定的系统中,幂等性检查是对重复的容忍,是对稳定性的追求,也是对失败的预期与接纳

系统设计分布式系统
Read More