边界值分析
1. 边界值分析原理#
1.1 基本概念#
边界值分析是基于”缺陷往往出现在输入边界”的经验,重点测试边界条件及其附近值。
它把「测哪里」从等价类的整个区间收敛到几个点,是等价类划分最常用的搭档。
1.2 为什么测试边界#
大量缺陷发生在边界条件上:
- 程序员使用
< 而非 <=
- 数组索引从 0 还是 1 开始
- 循环条件 off-by-one 错误
类比:边界像悬崖边缘——站在正中间怎么走都安全,几乎所有事故都发生在
离崖边一步的地方。代码同理:if (x > 0) 这类逻辑,只有在 x 逼近 0 时
才会暴露写错比较符的问题。
1.3 与等价类划分的关系#
边界值分析是等价类划分的补充,在等价类边界处选取测试值。标准流程:
先划分有效/无效等价类(见「等价类划分」),再在每个等价类的边界与
边界两侧取代表值。
2. 边界值选取#
2.1 基本边界值#
对于范围 :
| 值 | 描述 |
|---|
| 刚好小于下界 |
| 下界 |
| 刚好大于下界 |
| 正常值 | 范围内任意值 |
| 刚好小于上界 |
| 上界 |
| 刚好大于上界 |
2.2 示例:年龄 1-120#
| 值 | 类型 | 预期结果 |
|---|
| 0 | 无效(小于下界) | 错误 |
| 1 | 边界(下界) | 有效 |
| 2 | 边界(下界+1) | 有效 |
| 60 | 正常值 | 有效 |
| 119 | 边界(上界-1) | 有效 |
| 120 | 边界(上界) | 有效 |
| 121 | 无效(大于上界) | 错误 |
2.3 健壮性边界值#
在基本边界值基础上增加越界值:
| 值 | 描述 |
|---|
| 远小于下界 |
| 刚小于下界 |
| 下界 |
| 下界内侧 |
| 上界内侧 |
| 上界 |
| 刚大于上界 |
| 远大于上界 |
3. 边界值类型#
3.1 数值边界#
| 场景 | 边界值 |
|---|
| 范围 [0, 100] | -1, 0, 1, 99, 100, 101 |
| 整数范围 | INT_MIN, INT_MIN+1, INT_MAX-1, INT_MAX |
| 浮点数 | 0.0, 最小正值, 最大正值 |
3.2 字符串边界#
| 场景 | 边界值 |
|---|
| 长度 1-50 | "", “a”, “ab”, 49字符, 50字符, 51字符 |
| 空字符串 | "" |
| 仅空格 | ” ” |
3.3 集合边界#
| 场景 | 边界值 |
|---|
| 列表索引 | 0, 1, n-2, n-1 |
| 空列表 | [] |
| 单元素 | [1] |
3.4 时间边界#
| 场景 | 边界值 |
|---|
| 日期 | 1月1日, 12月31日, 2月28/29日 |
| 时间 | 00:00:00, 23:59:59 |
| 时区 | UTC-12, UTC+14 |
4. 边界值分析实例#
4.1 实例1:密码强度#
需求:密码长度 8-32,必须包含大小写字母和数字。
| 边界 | 长度 | 是否满足 | 预期 |
|---|
| 7 字符 | 7 | 否 | 拒绝 |
| 8 字符 | 8 | 是 | 接受 |
| 9 字符 | 9 | 是 | 接受 |
| 31 字符 | 31 | 是 | 接受 |
| 32 字符 | 32 | 是 | 接受 |
| 33 字符 | 33 | 否 | 拒绝 |
4.2 实例2:文件上传#
需求:文件大小 1KB-10MB,仅允许 jpg/png。
| 边界 | 大小 | 预期 |
|---|
| 0 KB | 空 | 拒绝 |
| 1 KB | 下界 | 接受 |
| 10 MB | 上界 | 接受 |
| 10MB+1KB | 超上界 | 拒绝 |
| .gif | 非法类型 | 拒绝 |
| .jpg | 合法类型 | 接受 |
4.3 实例3:分页查询#
需求:每页 10-100 条,页码 ≥ 1。
| 参数 | 边界值 | 预期 |
|---|
| page=0 | 无效 | 错误 |
| page=1 | 有效 | 第一页 |
| page=2 | 有效 | 第二页 |
| size=9 | 无效 | 错误 |
| size=10 | 有效 | 10条 |
| size=100 | 有效 | 100条 |
| size=101 | 无效 | 错误 |
5. 内部边界#
5.1 数据结构边界#
| 场景 | 边界 |
|---|
| 数组 | 空数组、单元素、满容量 |
| 栈 | 空栈、满栈、单元素 |
| 队列 | 空队列、满队列 |
| 哈希表 | 空表、冲突、扩容阈值 |
5.2 状态边界#
| 场景 | 边界 |
|---|
| 状态机 | 初始状态、最终状态 |
| 计数器 | 0、最大值、溢出 |
| 超时 | 刚好超时、刚好未超时 |
6. 边界值分析最佳实践#
| 实践 | 描述 |
|---|
| 识别所有边界 | 显式+隐式边界 |
| 测试两侧 | 边界内外都测 |
| 特殊值 | 0、空、null、负数 |
| 数据类型边界 | INT_MAX、浮点精度 |
| 业务边界 | 折扣阈值、等级分界 |
| 与等价类结合 | 先划分再取边界 |
6.1 可运行的完整示例#
import pytest
def withdraw_fee(months: int) -> int:
"""开户满 12 个月免手续费,否则收 2 元"""
if months >= 12:
return 0
return 2
@pytest.mark.parametrize("months, fee", [
(11, 2), # 下界 - 1(边界外侧)
(12, 0), # 下界(边界本身)
(13, 0), # 下界 + 1(边界内侧)
(-1, 2), # 健壮性:非法输入也要有确定行为
])
def test_withdraw_fee(months, fee):
assert withdraw_fee(months) == fee
注意断言的写法:每个参数只回答「这个边界值应得什么」,失败时
parametrize 的 id 会直接指出是哪条边界出了问题。
7. 常见陷阱#
- 只测边界本身,漏掉两侧:
< 与 <= 的区别必须靠「边界-1 / 边界 /
边界+1」三个值才能区分,只测边界值会把这类缺陷全部放过。
- 漏隐式边界:需求只写「金额上限 1 万」,隐式边界还有 0、负数、
小数位、极大值溢出。
- 硬编码边界值:用例里写死
assert fee(months=12) == 0,需求改成
13 个月时要改十处;把边界常量提取出来统一引用。
- 把边界值分析当穷举:每个边界 3-5 个代表值即可,目标是覆盖风险
而不是凑数量;组合爆炸的场景改用判定表或正交法。
- 初学者要点:每个区间边界取「边界-1、边界、边界+1」三个值;别忘了
0、空、负数、极大值这些通用特殊值;与等价类划分配合使用。
- 进阶注意:隐式边界(数据类型上限、状态机转换点、超时临界)往往比
显式需求边界更容易藏缺陷;用参数化框架落地边界用例,一条断言对应
一个边界点,失败信息即可定位到具体边界。