测试用例怎么写 功能边界测试的5个真实案例与实用方法总结
那天晚上我们发布了一个支付功能,用户输入”9999.99”提交,系统直接崩了。
排查下来发现,那个数值刚好等于数据库中某张表的字段上限,而我们的测试用例里从来没测过这个边界值。产品经理问了一句:”你们测试的时候,边界值都没想过吗?”
我哑口无言。
从那以后,边界测试成了我写测试用例的第一优先级。今天想跟你聊聊,边界测试到底该怎么玩,以及我踩过的那些坑、总结出来的那些方法。
一、边界测试的本质:为什么它这么重要
我先不说定义,问你一个问题:
一个输入框,允许用户输入1到100之间的数字。你会测哪些值?
很多人会测:1、2、3、50、99、100。
这没错,但这远远不够。
边界测试的核心思想是:错误往往藏在边界上,而不是藏在中间。
就像一座桥,中间那段你走起来很稳,但桥头桥尾最容易出事。软件测试也一样,用户输入1和100这两个边界值时,程序最容易翻车。
为什么?因为代码里常见的写法是:
# 很多开发者会这样写判断
if user_input >= 1 and user_input <= 100:
# 允许提交
else:
# 拒绝
看起来没问题对吧?但如果开发者写成:
if user_input > 1 and user_input < 100: # 少了等于号
# 允许提交
else:
# 拒绝
那1和100这两个值就会被错误地拒绝。这种bug,你不测边界,永远发现不了。
所以边界测试,就是在找那些”差点就对了,但就是差了那么一点点”的情况。
二、五个真实案例
案例一:手机号输入框的11位边界
背景:
我们做一个用户注册页,手机号输入框的限制是11位数字。
测试用例设计:
边界值分析:
- 最小有效边界:11位(刚好满足)
- 最大有效边界:11位(刚好满足)
- 最小无效边界:10位(少一位)
- 最大无效边界:12位(多一位)
测试用例:
1. 输入0位 → 应该禁用提交按钮
2. 输入10位 → 应该提示"手机号长度不足"
3. 输入11位 → 应该允许提交(这是一个正常手机号:13800138000)
4. 输入12位 → 应该提示"手机号长度超出限制"
5. 输入100位 → 应该只取前11位或直接拒绝
真实bug发现:
有一次,测试同学测了11位正常提交,测了12位被拒绝,都通过了。
但后来线上投诉:有用户输入11位手机号,系统返回”手机号格式错误”。
排查代码发现,开发用了一个正则:^\d{10,11}$。
这个正则的问题是,它匹配的是”10到11位”,但如果用户输入的是”138001380001”(12位),某些场景下正则引擎可能匹配到前11位,导致后续校验出错。
更关键的是,这个正则没有考虑手机号以”0”开头的情况。我们补了一个测试用例:
import re
def test_phone_validation():
phone_pattern = r'^1[3-9]\d{9}$'
# 边界测试用例
test_cases = [
("13800138000", True, "标准11位手机号"),
("03800138000", False, "以0开头,非法"),
("1380013800", False, "10位,少一位"),
("138001380001", False, "12位,多一位"),
("1380013800a", False, "含字母"),
("", False, "空字符串"),
("1" * 11, False, "11个1,不符合号段规则"),
]
for phone, expected, desc in test_cases:
result = bool(re.match(phone_pattern, phone))
status = "PASS" if result == expected else "FAIL"
print(f"[{status}] {desc}: {phone} -> {result} (预期{expected})")
这个测试跑下来,发现了一个问题:"1" * 11 也就是”11111111111”,正则匹配成功了,但这是一个不存在的号段。后来产品要求接入号段校验,这就是边界测试带来的连锁反应——一次边界测试,引发了一系列功能改进。
案例二:日期选择器的边界情况
背景:
一个机票预订系统,用户可以选择出发日期和返回日期。
测试用例设计:
日期边界分析:
- 今天日期(最小合法日期)
- 明天日期
- 昨天日期(应该不可选)
- 闰年的2月29日
- 大月(31天)的边界
- 跨月边界(如1月31日 → 2月1日)
- 跨年边界(12月31日 → 1月1日)
- 100年不是闰年但能被4整除的情况
- 400年能整除,是闰年
测试用例:
1. 出发日期选今天 → 应该提示"出发日期不能早于今天"
2. 出发日期选昨天 → 应该不可选
3. 出发日期选明天 → 正常可选
4. 出发日期选2024年2月29日(闰年)→ 正常可选
5. 出发日期选1900年2月29日(非闰年)→ 应该提示"日期无效"
6. 出发日期选2023年2月29日 → 应该提示"日期无效"
7. 出发日期选2024年1月31日,返回日期选2024年2月30日 → 应该提示"返回日期无效"
真实bug发现:
有一次,开发用了JavaScript的Date对象:
function validateDate(dateStr) {
const date = new Date(dateStr);
return !isNaN(date.getTime());
}
这个写法有一个经典的坑:JavaScript的Date对象会自动”进位”。比如你传”2023-02-30”,它不会报错,而是自动变成”2023-03-02”。
我们用测试用例发现了这个问题:
def test_date_validation_edge_cases():
"""测试各种日期边界的验证"""
from datetime import datetime
test_cases = [
("2023-02-30", False, "2月没有30号"),
("2024-02-29", True, "2024是闰年,2月有29号"),
("1900-02-29", False, "1900年不是闰年(能被100整除但不能被400整除)"),
("2000-02-29", True, "2000年是闰年(能被400整除)"),
("2023-04-31", False, "4月没有31号"),
("2023-06-31", False, "6月没有31号"),
("2023-12-32", False, "12月没有32号"),
("2024-12-31", True, "12月31日正常"),
("", False, "空字符串"),
("not-a-date", False, "非法字符串"),
]
for date_str, expected, desc in test_cases:
try:
if date_str:
parsed = datetime.strptime(date_str, "%Y-%m-%d")
result = parsed.strftime("%Y-%m-%d") == date_str
else:
result = False
status = "PASS" if result == expected else "FAIL"
except ValueError:
result = False
status = "PASS" if result == expected else "FAIL"
print(f"[{status}] {desc}: {date_str} -> {result} (预期{expected})")
这个测试跑出来,发现开发用的Date对象验证完全失效了。”2023-02-30”居然返回了true。最后我们改用了更严格的校验方式,并且在前端也加上了日期合法性检查。
案例三:权限系统的边界
背景:
一个后台管理系统,用户角色有:访客、普通用户、管理员、超级管理员。
权限规则:
- 访客只能看公开页面
- 普通用户可以编辑自己的内容
- 管理员可以管理所有用户
- 超级管理员拥有所有权限
测试用例设计:
权限边界分析:
- 角色边界:每种角色该有的权限和不该有的权限
- 权限叠加边界:一个用户有多个角色时,权限如何计算
- 权限降级边界:移除角色后的权限变化
- 权限提升边界:新增角色后的权限变化
- 空角色边界:没有任何角色的用户
- 权限边界:同一个资源,不同角色看到的内容差异
测试用例:
1. 访客访问管理后台 → 应该返回403
2. 普通用户删除其他用户 → 应该返回403
3. 管理员删除普通用户 → 应该成功
4. 管理员删除管理员 → 应该返回403(同级不能删同级)
5. 超级管理员删除任何角色 → 应该成功
6. 普通用户有管理员角色 → 应该具有管理员权限(权限叠加)
7. 管理员被移除管理员角色后 → 应该降级为普通用户
8. 没有任何角色的用户 → 应该只能访问公开页面
真实bug发现:
有一次,我们发现了一个严重的越权bug。
代码逻辑是这样的:
def check_permission(user, action, resource):
role = user.get('role')
if role == 'admin':
# 管理员可以操作所有资源
return True
if role == 'normal_user':
# 普通用户只能操作自己的资源
return resource.owner_id == user.id
# 默认拒绝
return False
看起来没问题?但问题在于,一个用户可能有多个角色。系统里的用户数据是这样的:
user = {
'id': 1001,
'roles': ['normal_user', 'admin'], # 同时有两个角色
'name': '张三'
}
但check_permission函数只取了user.get('role'),也就是只取了一个角色。在数据库中,这个角色字段存的是”normal_user”,而”admin”是存在另一个角色表里的。
这意味着,张三同时拥有普通用户和管理员角色,但系统只认了他的普通用户角色。他想删除其他管理员时,系统拒绝了。反之,如果一个普通用户被意外加了一个admin标记,他就能删除任何人。
我们补了一个完整的测试用例:
def test_permission_boundaries():
"""权限边界测试"""
# 用户数据
users = {
'guest': {'id': 1, 'roles': ['guest'], 'name': '访客'},
'normal': {'id': 2, 'roles': ['normal_user'], 'name': '普通用户'},
'admin': {'id': 3, 'roles': ['admin'], 'name': '管理员'},
'super_admin': {'id': 4, 'roles': ['super_admin'], 'name': '超级管理员'},
'multi_role': {'id': 5, 'roles': ['normal_user', 'admin'], 'name': '多角色用户'},
'no_role': {'id': 6, 'roles': [], 'name': '无角色用户'},
}
# 资源数据
resources = {
'public_page': {'type': 'public', 'owner_id': None},
'user_a_content': {'type': 'content', 'owner_id': 2},
'user_b_content': {'type': 'content', 'owner_id': 3},
'all_users': {'type': 'user_management', 'owner_id': None},
}
# 权限规则
def get_effective_roles(user):
"""获取用户的有效角色(考虑角色层级)"""
roles = user.get('roles', [])
if 'super_admin' in roles:
return ['super_admin']
# 多角色时,取最高权限
role_priority = {'super_admin': 4, 'admin': 3, 'normal_user': 2, 'guest': 1}
return sorted(roles, key=lambda r: role_priority.get(r, 0), reverse=True)[:1]
def check_permission(user, action, resource):
"""修正后的权限检查"""
effective_roles = get_effective_roles(user)
role = effective_roles[0] if effective_roles else 'guest'
if role == 'super_admin':
return True
if role == 'admin':
if action in ['create', 'read', 'update', 'delete']:
return True
return False
if role == 'normal_user':
if action == 'read':
return True
if action == 'update' and resource['owner_id'] == user['id']:
return True
return False
# guest和no_role
if action == 'read' and resource['type'] == 'public':
return True
return False
# 测试用例
test_cases = [
# (用户, 操作, 资源, 预期结果, 描述)
('guest', 'read', 'public_page', True, "访客可以读公开页面"),
('guest', 'delete', 'all_users', False, "访客不能删除用户"),
('normal', 'read', 'user_a_content', True, "普通用户可以读自己的内容"),
('normal', 'update', 'user_a_content', True, "普通用户可以更新自己的内容"),
('normal', 'update', 'user_b_content', False, "普通用户不能更新别人的内容"),
('normal', 'delete', 'user_a_content', False, "普通用户不能删除内容"),
('admin', 'delete', 'all_users', True, "管理员可以删除用户"),
('admin', 'delete', 'user_a_content', True, "管理员可以删除任何内容"),
('admin', 'delete', 'admin', False, "管理员不能删除管理员(同级保护)"),
('super_admin', 'delete', 'admin', True, "超级管理员可以删除任何人"),
('super_admin', 'delete', 'super_admin', True, "超级管理员权限无限制"),
('multi_role', 'delete', 'all_users', True, "多角色用户应具有最高权限"),
('no_role', 'read', 'public_page', True, "无角色用户可访问公开页面"),
('no_role', 'update', 'user_a_content', False, "无角色用户不能更新"),
]
for user_key, action, resource_key, expected, desc in test_cases:
user = users[user_key]
resource = resources[resource_key]
result = check_permission(user, action, resource)
status = "PASS" if result == expected else "FAIL"
print(f"[{status}] {desc}: {user['name']} {action} {resource_key} -> {result} (预期{expected})")
这个测试跑出来,我们发现”admin不能删除admin”这个规则没有被正确实现。修正后,我们加了一个同级保护逻辑,并且修复了多角色用户的权限计算bug。
案例四:数字输入的范围边界
背景:
一个积分商城,用户可以用积分兑换商品。积分规则:
- 最低兑换门槛:100积分
- 最高单次兑换上限:10000积分
- 积分最小单位:1积分
- 余额不足时提示”积分不足”
测试用例设计:
数字边界分析:
- 最小值边界:0、1、99、100
- 最大值边界:9999、10000、10001
- 负数边界:-1、-100
- 小数边界:0.5、1.5、99.9
- 极大值边界:99999999
- 类型边界:字符串"abc"、null、undefined
测试用例:
1. 输入0 → 应该提示"积分不能为0"
2. 输入1 → 应该提示"积分不足,最低100积分"
3. 输入99 → 应该提示"积分不足,最低100积分"
4. 输入100 → 应该正常兑换
5. 输入9999 → 应该正常兑换
6. 输入10000 → 应该正常兑换
7. 输入10001 → 应该提示"单次兑换上限为10000积分"
8. 输入-1 → 应该提示"积分不能为负数"
9. 输入0.5 → 应该提示"积分必须为整数"
10. 输入abc → 应该提示"请输入有效数字"
11. 输入空 → 应该提示"请输入兑换积分"
12. 输入99999999 → 应该提示"超出系统限制"
真实bug发现:
有一次,测试同学输入了10000.00,系统居然允许兑换了。
排查发现,后端的类型校验用的是弱类型语言(PHP):
// 有问题的代码
if ($points >= 100 && $points <= 10000) {
// 允许兑换
}
问题在于,10000.00在PHP里会被自动转换成整数10000,所以通过了校验。但更严重的问题是,如果用户输入10000.99,它会被转换成10000,导致用户实际支付了10000.99积分但只拿到了10000积分的商品。
我们用严格的测试用例发现了这个问题:
def test_points_exchange_boundary():
"""积分兑换边界测试"""
# 模拟后端验证逻辑
def validate_points_exchange(points_input):
"""验证积分兑换输入"""
# 类型检查
if isinstance(points_input, str):
try:
points = float(points_input)
except ValueError:
return False, "请输入有效数字"
elif isinstance(points_input, (int, float)):
points = float(points_input)
else:
return False, "请输入有效数字"
# 空值检查
if points is None or points == "":
return False, "请输入兑换积分"
# 负数检查
if points < 0:
return False, "积分不能为负数"
# 整数检查
if points != int(points):
return False, "积分必须为整数"
points = int(points)
# 范围检查
if points == 0:
return False, "积分不能为0"
if points < 100:
return False, f"积分不足,最低{100}积分"
if points > 10000:
return False, "单次兑换上限为10000积分"
return True, "验证通过"
# 测试用例
test_cases = [
(0, False, "积分不能为0"),
(1, False, "积分不足"),
(99, False, "积分不足"),
(100, True, "刚好满足最低门槛"),
(500, True, "正常兑换"),
(9999, True, "接近上限"),
(10000, True, "刚好满足上限"),
(10001, False, "超过上限"),
(-1, False, "负数"),
(-100, False, "负数"),
(0.5, False, "小数"),
(1.99, False, "小数"),
(100.00, True, "整数形式的小数"),
(10000.00, True, "整数形式的小数-上限"),
("abc", False, "非数字字符串"),
("", False, "空字符串"),
(None, False, "null值"),
(99999999, False, "极大值"),
("10000.99", False, "带小数的超限值"),
]
passed = 0
failed = 0
for input_val, expected_valid, desc in test_cases:
is_valid, message = validate_points_exchange(input_val)
status = "PASS" if is_valid == expected_valid else "FAIL"
if status == "PASS":
passed += 1
else:
failed += 1
print(f"[{status}] {desc}: 输入={repr(input_val)} -> 有效={is_valid} (预期={expected_valid}) 消息={message}")
print(f"\n测试总结: 通过={passed}, 失败={failed}, 总计={passed+failed}")
这个测试跑下来,发现了好几个问题。最严重的是一个安全漏洞:如果用户通过API直接调用,绕过前端校验,输入99999999,系统会返回”超过上限”,但错误信息里暴露了系统内部的上限值。我们加了一个通用的错误提示,不再透露具体的系统限制。
案例五:文件上传的大小边界
背景:
一个网盘系统,支持用户上传文件。限制:
- 单文件大小:最大100MB
- 文件类型:图片(jpg/png/gif)、文档(pdf/doc/docx)
- 文件名长度:最大255个字符
- 上传次数:每天最多100次
测试用例设计:
文件上传边界分析:
- 文件大小边界:0字节、1字节、100MB、100MB+1字节、1GB
- 文件类型边界:合法类型、非法类型、伪装类型
- 文件名边界:空名、255字符、256字符、特殊字符
- 上传次数边界:0次、99次、100次、101次
- 并发上传边界:同时上传多个文件
测试用例:
1. 上传0字节的文件 → 应该拒绝
2. 上传1字节的图片 → 应该正常上传
3. 上传99.99MB的图片 → 应该正常上传
4. 上传100MB的图片 → 应该正常上传(边界)
5. 上传100MB+1字节的图片 → 应该拒绝
6. 上传1GB的文件 → 应该拒绝
7. 上传.jpg文件 → 正常
8. 上传.jpg.txt文件(伪装)→ 应该拒绝
9. 上传空文件名 → 应该拒绝
10. 上传255字符的文件名 → 应该正常
11. 上传256字符的文件名 → 应该拒绝
12. 上传包含特殊字符的文件名 → 应该拒绝或转义
13. 一天内上传99次 → 正常
14. 一天内上传100次 → 应该正常(边界)
15. 一天内上传101次 → 应该拒绝
真实bug发现:
有一次,测试同学上传了一个100MB的文件,系统显示上传成功。但下载时发现文件不完整,只有99MB。
排查发现,后端用了一个32位整数来存储文件大小:
// 有问题的代码
int fileSize = file.getBytes().length; // 32位整数
if (fileSize > 100 * 1024 * 1024) {
throw new BusinessException("文件大小超过限制");
}
32位整数的最大值是2^31-1 ≈ 2.1GB,理论上够用了。但问题在于,文件分割上传时,每个分片的大小也是用int存储的。当一个100MB的文件被分成10个10MB的分片时,最后一个分片的校验出现了问题。
另外,还有一个更隐蔽的bug:前端用JavaScript计算文件大小,JavaScript的数字精度问题导致100MB的文件被判断为99.99MB,从而绕过了前端的校验。
我们用测试用例发现了这些问题:
def test_file_upload_boundaries():
"""文件上传边界测试"""
# 模拟后端验证
MAX_FILE_SIZE = 100 * 1024 * 1024 # 100MB
MAX_FILENAME_LENGTH = 255
MAX_DAILY_UPLOADS = 100
allowed_extensions = {'.jpg', '.jpeg', '.png', '.gif', '.pdf', '.doc', '.docx'}
def validate_file_upload(file_size, file_name, file_extension, daily_uploads):
"""验证文件上传"""
errors = []
# 文件大小检查
if file_size <= 0:
errors.append("文件大小不能为0或负数")
elif file_size > MAX_FILE_SIZE:
errors.append(f"文件大小不能超过{MAX_FILE_SIZE // (1024*1024)}MB")
# 文件名检查
if not file_name or file_name.strip() == "":
errors.append("文件名不能为空")
elif len(file_name) > MAX_FILENAME_LENGTH:
errors.append(f"文件名长度不能超过{MAX_FILENAME_LENGTH}个字符")
# 文件类型检查
ext = os.path.splitext(file_name)[1].lower()
if ext not in allowed_extensions:
errors.append(f"不支持的文件类型: {ext}")
# 检查伪装类型(文件名和后缀不匹配)
if ext == '.txt' and file_name.endswith('.jpg.txt'):
errors.append("不允许使用伪装文件类型")
# 上传次数检查
if daily_uploads >= MAX_DAILY_UPLOADS:
errors.append(f"今日上传次数已达上限({MAX_DAILY_UPLOADS}次)")
return len(errors) == 0, errors
# 测试用例
test_cases = [
# (file_size, file_name, extension, daily_uploads, expected_valid, description)
(0, "test.jpg", ".jpg", 0, False, "0字节文件"),
(1, "test.jpg", ".jpg", 0, True, "1字节文件"),
(99 * 1024 * 1024, "test.jpg", ".jpg", 0, True, "99MB文件"),
(100 * 1024 * 1024, "test.jpg", ".jpg", 0, True, "刚好100MB"),
(100 * 1024 * 1024 + 1, "test.jpg", ".jpg", 0, False, "100MB+1字节"),
(1024 * 1024 * 1024, "test.jpg", ".jpg", 0, False, "1GB文件"),
(50 * 1024 * 1024, "", ".jpg", 0, False, "空文件名"),
(50 * 1024 * 1024, "a" * 255, ".jpg", 0, True, "255字符文件名"),
(50 * 1024 * 1024, "a" * 256, ".jpg", 0, False, "256字符文件名"),
(50 * 1024 * 1024, "test.jpg.txt", ".txt", 0, False, "伪装文件类型"),
(50 * 1024 * 1024, "test.exe", ".exe", 0, False, "可执行文件"),
(50 * 1024 * 1024, "test.jpg", ".jpg", 99, True, "99次上传"),
(50 * 1024 * 1024, "test.jpg", ".jpg", 100, False, "100次上传-达到上限"),
(50 * 1024 * 1024, "test.jpg", ".jpg", 101, False, "101次上传-超过上限"),
(50 * 1024 * 1024, "test.jpg", ".jpg", 0, True, "正常上传"),
]
import os
passed = 0
failed = 0
for file_size, file_name, ext, daily_uploads, expected_valid, desc in test_cases:
is_valid, errors = validate_file_upload(file_size, file_name, ext, daily_uploads)
status = "PASS" if is_valid == expected_valid else "FAIL"
if status == "PASS":
passed += 1
else:
failed += 1
error_msg = ", ".join(errors) if errors else "无错误"
print(f"[{status}] {desc}: 有效={is_valid} (预期={expected_valid}) 错误={error_msg}")
print(f"\n测试总结: 通过={passed}, 失败={failed}, 总计={passed+failed}")
这个测试发现了几个问题:
- 伪装文件类型没有被正确检测
- 上传次数的边界判断用的是
>=而不是>,导致第100次上传被错误拒绝 - 文件名长度检查没有考虑UTF-8编码下中文字符占3个字节的实际情况
修复后,我们增加了对文件魔数(magic number)的检查,不再只依赖文件扩展名来判断文件类型。
三、边界测试的实用方法总结
经过这么多次踩坑,我总结出了一套边界测试的方法论,叫“边界五步法”:
第一步:识别边界
拿到一个功能,先问自己:这个功能的输入有什么边界?
常见的边界类型:
- 数值边界:最小值、最大值、正数、负数、零
- 长度边界:最小长度、最大长度
- 时间边界:今天、明天、昨天、闰年、跨月、跨年
- 状态边界:有/无、是/否、启用/禁用
- 数量边界:0、1、最大数量、最小数量
- 权限边界:有无权限、权限级别、权限叠加
第二步:确定边界值
对于每个边界,确定有效边界和无效边界:
以"1-100"的范围为例:
有效边界:1(最小有效值)、100(最大有效值)
无效边界:0(刚好小于最小)、101(刚好大于最大)
这个思路可以推广到所有场景。
第三步:设计测试用例
每个边界至少设计两个测试用例:
- 一个测试有效边界(应该成功)
- 一个测试无效边界(应该失败)
如果边界比较复杂,可以设计等价类划分:
- 有效等价类:1-100之间的所有值
- 无效等价类1:小于1的值
- 无效等价类2:大于100的值
第四步:编写可执行的测试代码
不要只写文字用例,要写成可以自动执行的测试代码。这样的好处是:
- 可以随时回归测试
- 可以减少人为错误
- 可以集成到CI/CD流程中
第五步:持续补充边界用例
每次发现bug,都要反思:这个bug是不是边界问题?
如果是,就把这个边界加到你的测试用例库里。这样,下次类似的功能,你就不用从零开始想边界了。
四、几个实用的边界测试技巧
技巧一:用极端值测试
不要只测正常边界,还要测极端边界。比如:
- 空值
- 极大值
- 极小值
- null
- undefined
- 特殊字符
技巧二:考虑编码问题
边界测试要考虑不同编码下的表现。比如:
- UTF-8下,一个中文字符占3个字节
- 文件名长度是按字符算还是按字节算?
- 数据库字段长度是按字符还是按字节?
技巧三:考虑并发边界
有些边界在单线程下没问题,但在并发下会出问题。比如:
- 库存扣减:两个用户同时购买最后一个商品
- 积分扣减:两个用户同时用同样的积分兑换同一个商品
- 上传次数:多个请求同时触发,绕过频率限制
技巧四:考虑精度问题
浮点数运算会有精度问题,边界测试时要特别注意:
# 浮点数精度问题
print(0.1 + 0.2) # 输出 0.30000000000000004
print(19.99 + 10.01) # 可能不等于 30.0
技巧五:考虑时区边界
日期相关的边界测试要考虑时区:
- UTC和本地时间的转换
- 跨时区的日期边界(比如北京时间1月1日00:00,对应UTC时间12月31日16:00)
五、最后的建议
边界测试不是一次性的工作,而是一个持续的过程。
每次发现bug,都要问自己:这个bug是不是边界问题?有没有可能还有其他类似的边界我没考虑到?
把你的边界测试用例整理成一个知识库,每次新需求都要对照这个知识库,检查一下有没有遗漏的边界。
另外,边界测试也要和开发同学沟通。很多边界问题,开发在写代码的时候就可以避免。比如:
- 用枚举代替魔法数字
- 用类型注解代替隐式类型转换
- 用断言代替隐式假设
好了,以上就是我这些年做边界测试的一些经验和总结。希望对你有用。如果有什么疑问,欢迎在评论区讨论。
