目录

测试用例设计的艺术
测试用例设计概述
什么是测试用例设计?
测试用例设计是软件测试中最核心的技能之一,它是根据需求规格、设计文档或用户故事,运用特定的测试设计技术,设计出能够发现软件缺陷的测试场景和数据的过程。
优秀测试用例的特征
✅ 完整性:覆盖所有需求和功能点; ✅ 有效性:能够发现潜在的缺陷
✅ 独立性:用例之间相互独立,不依赖执行顺序;✅ 可重复性:在相同条件下能够重复执行
✅ 简洁性:步骤清晰,易于理解和执行; ✅ 可维护性:便于修改和更新
测试用例设计的基本流程
需求分析--> 测试分析 --> 选择设计方法 -->设计测试用例 --> 评审优化 --> 执行验证 --> 维护更新
测试用例模板
| 基本信息 | - 用例编号:TC_001; - 用例名称:用户登录功能验证 |
| 测试描述 | - 测试目标:验证用户能够正常登录系统 - 前置条件:系统正常运行,用户账号已注册 - 测试环境:测试环境 |
| 测试步骤 | 详写每一步步骤,前置条件-测试数据-预期结果 |
| 后置条件 | - 用户处于登录状态 ;- 清理测试数据(如需要) |
测试种常用的方法
等价类划分法
等价类划分法是把所有可能的输入数据,即程序的输入域划分成若干部分(子集),然后从每个子集中选取少数具有代表性的数据作为测试用例。
等价类分类
有效等价类:输入域中对程序规格说明是合理的、有意义的输入数据构成的集合。
无效等价类:输入域中对程序规格说明是不合理的、无意义的输入数据构成的集合。
等价类划分步骤
def equivalence_class_design():
"""
等价类划分设计步骤
"""
steps = [
"1. 分析需求,确定输入条件和输出条件",
"2. 确定输入条件的有效等价类和无效等价类",
"3. 为每个等价类编号",
"4. 设计测试用例覆盖所有等价类",
"5. 设计测试用例时尽量同时覆盖多个有效等价类",
"6. 无效等价类需要单独设计测试用例"
]
return steps
实例1:年龄输入验证
需求:系统要求输入年龄,有效范围为18-65岁
# 等价类划分
## 有效等价类
- EC1:18 ≤ 年龄 ≤ 65 (整数)
## 无效等价类
- EC2:年龄 < 18
- EC3:年龄 > 65
- EC4:非数字字符
- EC5:小数
- EC6:负数
- EC7:空值
- EC8:特殊字符
# 测试用例设计
| 用例编号 | 覆盖等价类 | 输入数据 | 预期结果 |
|----------|------------|----------|----------|
| TC001 | EC1 | 25 | 输入有效,接受 |
| TC002 | EC1 | 18 | 输入有效,接受 |
| TC003 | EC1 | 65 | 输入有效,接受 |
| TC004 | EC2 | 17 | 提示年龄过小 |
| TC005 | EC3 | 66 | 提示年龄过大 |
| TC006 | EC4 | abc | 提示输入格式错误 |
| TC007 | EC5 | 25.5 | 提示必须为整数 |
| TC008 | EC6 | -5 | 提示年龄不能为负数 |
| TC009 | EC7 | (空) | 提示不能为空 |
| TC010 | EC8 | @#$ | 提示输入格式错误 |
实例2:用户注册表单
需求:用户注册需要填写用户名、邮箱、密码
class RegistrationTestCase:
"""用户注册测试用例设计"""
def __init__(self):
self.username_valid = ["user123", "test_user", "a1b2c3"] # 6-20位字母数字下划线
self.username_invalid_short = ["ab", "123"] # 少于6位
self.username_invalid_long = ["a" * 21] # 超过20位
self.username_invalid_char = ["user@123", "user 123"] # 非法字符
self.email_valid = ["test@example.com", "user123@gmail.com"]
self.email_invalid = ["test", "@example.com", "test@", "test@.com"]
self.password_valid = ["Password123!", "Abc12345"] # 8-16位包含大小写字母数字
self.password_invalid_short = ["123"] # 少于8位
self.password_invalid_long = ["a" * 17] # 超过16位
self.password_invalid_weak = ["password", "12345678"] # 不符合复杂度要求
def design_test_cases(self):
"""设计测试用例"""
test_cases = [
# 有效等价类组合
{"id": "TC001", "username": "user123", "email": "test@example.com",
"password": "Password123!", "expected": "注册成功"},
# 用户名无效等价类
{"id": "TC002", "username": "ab", "email": "test@example.com",
"password": "Password123!", "expected": "用户名长度不符合要求"},
{"id": "TC003", "username": "user@123", "email": "test@example.com",
"password": "Password123!", "expected": "用户名包含非法字符"},
# 邮箱无效等价类
{"id": "TC004", "username": "user123", "email": "invalid_email",
"password": "Password123!", "expected": "邮箱格式错误"},
# 密码无效等价类
{"id": "TC005", "username": "user123", "email": "test@example.com",
"password": "123", "expected": "密码长度不符合要求"},
{"id": "TC006", "username": "user123", "email": "test@example.com",
"password": "password", "expected": "密码复杂度不符合要求"}
]
return test_cases
边界值分析法
边界值分析法是对等价类划分法的补充,通过对等价类的边界值进行测试来查找错误。边界值分析基于这样的观察:大量的错误是发生在输入或输出范围的边界上。
边界值选择原则
def boundary_value_selection():
"""边界值选择原则"""
principles = {
"刚好等于边界值": "正好在边界上的值",
"刚刚大于边界值": "边界值+1",
"刚刚小于边界值": "边界值-1",
"远大于边界值": "远远超过边界的值",
"远小于边界值": "远远小于边界的值"
}
return principles
单变量边界值分析
示例:密码长度验证(8-16位)
# 边界值分析
## 输入范围:8-16位
- 最小值-1:7位
- 最小值:8位
- 最小值+1:9位
- 中间值:12位
- 最大值-1:15位
- 最大值:16位
- 最大值+1:17位
# 测试用例
| 用例编号 | 输入长度 | 测试数据 | 预期结果 |
|----------|----------|----------|----------|
| TC001 | 7位 | 1234567 | 密码长度过短 |
| TC002 | 8位 | 12345678 | 密码有效 |
| TC003 | 9位 | 123456789 | 密码有效 |
| TC004 | 12位 | 123456789012 | 密码有效 |
| TC005 | 15位 | 123456789012345 | 密码有效 |
| TC006 | 16位 | 1234567890123456 | 密码有效 |
| TC007 | 17位 | 12345678901234567 | 密码长度过长 |
多变量边界值分析
示例:图片上传功能
class ImageUploadBoundaryTest:
"""图片上传边界值测试"""
def __init__(self):
# 文件大小限制:1KB - 5MB
self.size_boundaries = {
"min_minus": 1023, # 1KB - 1
"min": 1024, # 1KB
"min_plus": 1025, # 1KB + 1
"max_minus": 5242879, # 5MB - 1
"max": 5242880, # 5MB
"max_plus": 5242881 # 5MB + 1
}
# 尺寸限制:100x100 - 2000x2000
self.dimension_boundaries = {
"width": {"min": 100, "max": 2000},
"height": {"min": 100, "max": 2000}
}
def design_boundary_cases(self):
"""设计边界值测试用例"""
cases = []
# 文件大小边界测试
for key, size in self.size_boundaries.items():
cases.append({
"case_id": f"Size_{key}",
"file_size": size,
"width": 500, # 正常值
"height": 500, # 正常值
"expected": "成功" if "min" <= key <= "max" else "失败"
})
# 尺寸边界测试 - 宽度
width_tests = [99, 100, 101, 1999, 2000, 2001]
for i, width in enumerate(width_tests):
cases.append({
"case_id": f"Width_boundary_{i+1}",
"file_size": 1048576, # 1MB 正常值
"width": width,
"height": 500, # 正常值
"expected": "成功" if 100 <= width <= 2000 else "失败"
})
# 尺寸边界测试 - 高度
height_tests = [99, 100, 101, 1999, 2000, 2001]
for i, height in enumerate(height_tests):
cases.append({
"case_id": f"Height_boundary_{i+1}",
"file_size": 1048576, # 1MB 正常值
"width": 500, # 正常值
"height": height,
"expected": "成功" if 100 <= height <= 2000 else "失败"
})
return cases
边界值分析实践技巧
def boundary_testing_tips():
"""边界值测试实践技巧"""
tips = {
"数值边界": [
"整数范围的最大值、最小值",
"浮点数的精度边界",
"零值和负数边界"
],
"字符串边界": [
"空字符串",
"单字符字符串",
"最大长度字符串",
"超长字符串"
],
"日期时间边界": [
"年份边界(1900,2000,2038等)",
"月份边界(1月,12月)",
"日期边界(月初,月末,闰年2月29日)",
"时间边界(00:00:00,23:59:59)"
],
"集合边界": [
"空集合",
"单元素集合",
"最大容量集合",
"超出容量限制"
]
}
return tips
# 日期边界值测试示例
def date_boundary_test_cases():
"""日期边界值测试用例"""
test_dates = [
# 年份边界
{"date": "1999-12-31", "desc": "20世纪最后一天"},
{"date": "2000-01-01", "desc": "21世纪第一天"},
{"date": "2038-01-19", "desc": "Unix时间戳上限"},
# 月份边界
{"date": "2024-01-01", "desc": "年初第一天"},
{"date": "2024-12-31", "desc": "年末最后一天"},
# 闰年边界
{"date": "2024-02-29", "desc": "闰年2月29日"},
{"date": "2023-02-29", "desc": "非闰年2月29日(无效)"},
# 月末边界
{"date": "2024-01-31", "desc": "1月最后一天"},
{"date": "2024-04-31", "desc": "4月31日(无效)"},
{"date": "2024-06-31", "desc": "6月31日(无效)"}
]
return test_dates
判定表法
判定表法是分析和表达多逻辑条件下执行不同操作的情况的工具。当程序的逻辑关系比较复杂时,采用判定表能够清楚地表达复杂的逻辑关系。
判定表组成
# 判定表结构
┌─────────────┬─────────────────────────────────┐
│ 条件桩 │ 条件项 │
├─────────────┼─────────────────────────────────┤
│ 动作桩 │ 动作项 │
└─────────────┴─────────────────────────────────┘
- 条件桩:列出问题的所有条件
- 条件项:列出所有条件的取值情况
- 动作桩:列出问题规定的所有可能动作
- 动作项:列出在条件项各种取值情况下应该采取的动作
判定表设计步骤
def decision_table_steps():
"""判定表设计步骤"""
steps = [
"1. 确定规则的个数:如果有n个条件,每个条件有2个取值,则有2^n种规则",
"2. 列出所有条件桩和动作桩",
"3. 填入条件项:列举出所有条件的组合",
"4. 填入动作项:根据条件组合确定相应的动作",
"5. 简化判定表:合并相似的规则,消除冗余",
"6. 设计测试用例:每一列代表一个测试用例"
]
return steps
实例1:登录验证判定表
需求: 用户登录需要验证用户名、密码和验证码
# 登录验证判定表
| 条件/动作 | 规则1 | 规则2 | 规则3 | 规则4 | 规则5 | 规则6 | 规则7 | 规则8 |
|-----------|-------|-------|-------|-------|-------|-------|-------|-------|
| **条件** |
| 用户名正确 | Y | Y | Y | Y | N | N | N | N |
| 密码正确 | Y | Y | N | N | Y | Y | N | N |
| 验证码正确 | Y | N | Y | N | Y | N | Y | N |
| **动作** |
| 登录成功 | ✓ | | | | | | | |
| 提示验证码错误 | | ✓ | | ✓ | | ✓ | | ✓ |
| 提示密码错误 | | | ✓ | | | | ✓ | |
| 提示用户名错误 | | | | | ✓ | | | |
| 登录失败 | | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
# 测试用例
| 用例编号 | 用户名 | 密码 | 验证码 | 预期结果 |
|----------|--------|------|--------|----------|
| TC001 | 正确 | 正确 | 正确 | 登录成功 |
| TC002 | 正确 | 正确 | 错误 | 验证码错误 |
| TC003 | 正确 | 错误 | 正确 | 密码错误 |
| TC004 | 正确 | 错误 | 错误 | 密码错误 |
| TC005 | 错误 | 正确 | 正确 | 用户名错误 |
| TC006 | 错误 | 正确 | 错误 | 用户名错误 |
| TC007 | 错误 | 错误 | 正确 | 用户名错误 |
| TC008 | 错误 | 错误 | 错误 | 用户名错误 |
实例2:订单折扣计算
需求:
- VIP用户享受9折优惠
- 订单金额超过1000元享受95折优惠
- 使用优惠券额外减50元
- 优惠可以叠加
class DiscountDecisionTable:
"""订单折扣计算判定表"""
def __init__(self):
self.conditions = ["是否VIP用户", "订单金额>1000", "是否使用优惠券"]
self.actions = ["VIP折扣9折", "大额折扣95折", "优惠券减50元", "无优惠"]
def create_decision_table(self):
"""创建判定表"""
# 条件组合:2^3 = 8种
rules = [
{"vip": True, "amount_gt_1000": True, "coupon": True}, # 规则1
{"vip": True, "amount_gt_1000": True, "coupon": False}, # 规则2
{"vip": True, "amount_gt_1000": False, "coupon": True}, # 规则3
{"vip": True, "amount_gt_1000": False, "coupon": False}, # 规则4
{"vip": False, "amount_gt_1000": True, "coupon": True}, # 规则5
{"vip": False, "amount_gt_1000": True, "coupon": False}, # 规则6
{"vip": False, "amount_gt_1000": False, "coupon": True}, # 规则7
{"vip": False, "amount_gt_1000": False, "coupon": False} # 规则8
]
# 动作映射
actions = [
["VIP9折", "大额95折", "优惠券-50"], # 规则1:三重优惠
["VIP9折", "大额95折"], # 规则2:双重折扣
["VIP9折", "优惠券-50"], # 规则3:VIP+优惠券
["VIP9折"], # 规则4:仅VIP
["大额95折", "优惠券-50"], # 规则5:大额+优惠券
["大额95折"], # 规则6:仅大额
["优惠券-50"], # 规则7:仅优惠券
["无优惠"] # 规则8:无优惠
]
return rules, actions
def generate_test_cases(self):
"""生成测试用例"""
rules, actions = self.create_decision_table()
test_cases = []
for i, (rule, action) in enumerate(zip(rules, actions)):
# 根据条件确定测试数据
amount = 1500 if rule["amount_gt_1000"] else 800
test_case = {
"case_id": f"TC{i+1:03d}",
"vip_status": "VIP" if rule["vip"] else "普通用户",
"order_amount": amount,
"has_coupon": rule["coupon"],
"expected_discount": action,
"description": self._generate_description(rule, action)
}
test_cases.append(test_case)
return test_cases
def _generate_description(self, rule, action):
"""生成测试用例描述"""
conditions = []
if rule["vip"]:
conditions.append("VIP用户")
if rule["amount_gt_1000"]:
conditions.append("大额订单")
if rule["coupon"]:
conditions.append("使用优惠券")
if not conditions:
return "普通用户小额订单无优惠券"
return f"{'+'.join(conditions)}场景"
判定表简化技术
def simplify_decision_table():
"""判定表简化技术"""
techniques = {
"合并规则": "如果两个规则的条件项只有一个不同,且动作项完全相同,可以合并",
"消除冗余": "删除不可能的条件组合",
"优先级排序": "将最重要的条件放在前面",
"分组处理": "将相似的规则分组,简化表结构"
}
# 简化示例:登录验证表
simplified_example = """
简化前8个规则 → 简化后4个规则:
规则1:用户名正确 && 密码正确 && 验证码正确 → 登录成功
规则2:用户名正确 && 密码正确 && 验证码错误 → 验证码错误
规则3:用户名正确 && 密码错误 && (验证码任意) → 密码错误
规则4:用户名错误 && (密码任意) && (验证码任意) → 用户名错误
"""
return techniques, simplified_example
状态转换法
状态转换法是通过分析系统的状态变化来设计测试用例的方法。它特别适用于测试系统在不同状态下的行为,以及状态之间的转换是否正确。
状态转换图组成
# 状态转换图要素
- 状态(State):系统在某一时刻的条件或情况
- 事件(Event):引起状态改变的外部刺激
- 转换(Transition):从一个状态到另一个状态的变化
- 动作(Action):状态转换时执行的操作
- 保护条件(Guard):状态转换的前提条件
状态转换测试设计步骤
def state_transition_steps():
"""状态转换测试设计步骤"""
steps = [
"1. 分析系统需求,识别所有可能的状态",
"2. 确定状态之间的转换条件和事件",
"3. 绘制状态转换图或状态转换表",
"4. 识别所有有效的状态转换路径",
"5. 识别无效的状态转换(负面测试)",
"6. 设计测试用例覆盖所有转换路径",
"7. 确保每个状态都能到达和离开"
]
return steps
实例1:ATM机状态转换
class ATMStateMachine:
"""ATM机状态转换测试"""
def __init__(self):
self.states = {
"IDLE": "空闲状态",
"CARD_INSERTED": "卡片已插入",
"PIN_ENTERED": "PIN已输入",
"AUTHENTICATED": "认证成功",
"TRANSACTION": "交易中",
"DISPENSING": "出钞中",
"RECEIPT": "打印凭条",
"CARD_EJECTED": "退卡"
}
self.events = {
"insert_card": "插入银行卡",
"enter_pin": "输入PIN码",
"pin_correct": "PIN码正确",
"pin_incorrect": "PIN码错误",
"select_withdraw": "选择取款",
"enter_amount": "输入金额",
"confirm": "确认交易",
"dispense_cash": "出钞",
"print_receipt": "打印凭条",
"eject_card": "退卡",
"timeout": "超时",
"cancel": "取消"
}
def create_state_transitions(self):
"""创建状态转换表"""
transitions = [
# (当前状态, 事件, 目标状态, 动作)
("IDLE", "insert_card", "CARD_INSERTED", "读取卡片信息"),
("CARD_INSERTED", "enter_pin", "PIN_ENTERED", "验证PIN码"),
("PIN_ENTERED", "pin_correct", "AUTHENTICATED", "认证成功"),
("PIN_ENTERED", "pin_incorrect", "CARD_INSERTED", "提示重新输入"),
("AUTHENTICATED", "select_withdraw", "TRANSACTION", "显示取款界面"),
("TRANSACTION", "enter_amount", "TRANSACTION", "验证金额"),
("TRANSACTION", "confirm", "DISPENSING", "准备出钞"),
("DISPENSING", "dispense_cash", "RECEIPT", "出钞完成"),
("RECEIPT", "print_receipt", "CARD_EJECTED", "打印凭条"),
("CARD_EJECTED", "eject_card", "IDLE", "退卡完成"),
# 异常转换
("CARD_INSERTED", "timeout", "IDLE", "超时退卡"),
("PIN_ENTERED", "timeout", "IDLE", "超时退卡"),
("AUTHENTICATED", "timeout", "IDLE", "超时退卡"),
("TRANSACTION", "cancel", "CARD_EJECTED", "取消交易"),
("*", "card_removed", "IDLE", "强制退出")
]
return transitions
def generate_test_paths(self):
"""生成测试路径"""
test_paths = [
{
"path_id": "P001",
"description": "正常取款流程",
"path": "IDLE → CARD_INSERTED → PIN_ENTERED → AUTHENTICATED → TRANSACTION → DISPENSING → RECEIPT → CARD_EJECTED → IDLE",
"events": ["insert_card", "enter_pin", "pin_correct", "select_withdraw", "confirm", "dispense_cash", "print_receipt", "eject_card"]
},
{
"path_id": "P002",
"description": "PIN码错误重试",
"path": "IDLE → CARD_INSERTED → PIN_ENTERED → CARD_INSERTED → PIN_ENTERED → AUTHENTICATED",
"events": ["insert_card", "enter_pin", "pin_incorrect", "enter_pin", "pin_correct"]
},
{
"path_id": "P003",
"description": "交易中取消",
"path": "IDLE → CARD_INSERTED → PIN_ENTERED → AUTHENTICATED → TRANSACTION → CARD_EJECTED → IDLE",
"events": ["insert_card", "enter_pin", "pin_correct", "select_withdraw", "cancel", "eject_card"]
},
{
"path_id": "P004",
"description": "认证后超时",
"path": "IDLE → CARD_INSERTED → PIN_ENTERED → AUTHENTICATED → IDLE",
"events": ["insert_card", "enter_pin", "pin_correct", "timeout"]
}
]
return test_paths
实例2:订单状态转换
class OrderStateMachine:
"""订单状态转换测试"""
def __init__(self):
self.states = {
"PENDING": "待付款",
"PAID": "已付款",
"CONFIRMED": "已确认",
"SHIPPED": "已发货",
"DELIVERED": "已送达",
"COMPLETED": "已完成",
"CANCELLED": "已取消",
"REFUNDED": "已退款"
}
def create_state_transition_table(self):
"""创建状态转换表"""
transitions = {
"PENDING": {
"pay": "PAID",
"cancel": "CANCELLED",
"timeout": "CANCELLED"
},
"PAID": {
"confirm": "CONFIRMED",
"refund": "REFUNDED"
},
"CONFIRMED": {
"ship": "SHIPPED",
"cancel": "CANCELLED"
},
"SHIPPED": {
"deliver": "DELIVERED",
"return": "CONFIRMED" # 拒收退回
},
"DELIVERED": {
"complete": "COMPLETED",
"return": "REFUNDED" # 退货退款
},
"COMPLETED": {
# 终态,无转换
},
"CANCELLED": {
# 终态,无转换
},
"REFUNDED": {
# 终态,无转换
}
}
return transitions
def design_state_test_cases(self):
"""设计状态转换测试用例"""
test_cases = [
# 正常流程测试
{
"case_id": "ST001",
"description": "正常购买流程",
"initial_state": "PENDING",
"events": ["pay", "confirm", "ship", "deliver", "complete"],
"expected_path": "PENDING→PAID→CONFIRMED→SHIPPED→DELIVERED→COMPLETED"
},
# 取消流程测试
{
"case_id": "ST002",
"description": "付款前取消",
"initial_state": "PENDING",
"events": ["cancel"],
"expected_path": "PENDING→CANCELLED"
},
{
"case_id": "ST003",
"description": "付款后申请退款",
"initial_state": "PENDING",
"events": ["pay", "refund"],
"expected_path": "PENDING→PAID→REFUNDED"
},
# 异常流程测试
{
"case_id": "ST004",
"description": "已发货状态拒收",
"initial_state": "PENDING",
"events": ["pay", "confirm", "ship", "return"],
"expected_path": "PENDING→PAID→CONFIRMED→SHIPPED→CONFIRMED"
},
{
"case_id": "ST005",
"description": "已送达申请退货",
"initial_state": "PENDING",
"events": ["pay", "confirm", "ship", "deliver", "return"],
"expected_path": "PENDING→PAID→CONFIRMED→SHIPPED→DELIVERED→REFUNDED"
},
# 无效转换测试
{
"case_id": "ST006",
"description": "已完成订单尝试取消(无效操作)",
"initial_state": "COMPLETED",
"events": ["cancel"],
"expected_result": "操作失败,状态不变"
}
]
return test_cases
状态转换覆盖准则
def state_coverage_criteria():
"""状态转换覆盖准则"""
criteria = {
"状态覆盖": {
"定义": "每个状态至少被访问一次",
"适用": "基本的状态测试"
},
"转换覆盖": {
"定义": "每个状态转换至少被执行一次",
"适用": "完整的转换测试"
},
"路径覆盖": {
"定义": "覆盖所有可能的状态转换路径",
"适用": "全面的路径测试"
},
"开关覆盖": {
"定义": "每个状态的每个转换条件都要测试",
"适用": "条件分支测试"
}
}
# 测试路径生成算法示例
def generate_all_paths(transitions, start_state, max_length=5):
"""生成所有可能的测试路径"""
paths = []
def dfs(current_state, path, visited):
if len(path) > max_length:
return
if current_state in transitions:
for event, next_state in transitions[current_state].items():
new_path = path + [(current_state, event, next_state)]
# 避免无限循环
if next_state not in visited or len(path) < 3:
new_visited = visited.copy()
new_visited.add(next_state)
paths.append(new_path)
dfs(next_state, new_path, new_visited)
dfs(start_state, [], {start_state})
return paths
return criteria
正交试验法
正交试验法是从大量的试验点中挑选出适量的、有代表性的试验点,通过这些试验点来达到考察全面情况的目的。在软件测试中,正交试验法可以用较少的测试用例覆盖尽可能多的功能组合。
正交表基础
class OrthogonalTesting:
"""正交试验法测试设计"""
def __init__(self):
# 常用正交表
self.orthogonal_arrays = {
"L4(2^3)": [
[1, 1, 1], # 4个试验,3个因子,每个因子2水平
[1, 2, 2],
[2, 1, 2],
[2, 2, 1]
],
"L8(2^7)": [
[1, 1, 1, 1, 1, 1, 1], # 8个试验,7个因子,每个因子2水平
[1, 1, 1, 2, 2, 2, 2],
[1, 2, 2, 1, 1, 2, 2],
[1, 2, 2, 2, 2, 1, 1],
[2, 1, 2, 1, 2, 1, 2],
[2, 1, 2, 2, 1, 2, 1],
[2, 2, 1, 1, 2, 2, 1],
[2, 2, 1, 2, 1, 1, 2]
],
"L9(3^4)": [
[1, 1, 1, 1], # 9个试验,4个因子,每个因子3水平
[1, 2, 2, 2],
[1, 3, 3, 3],
[2, 1, 2, 3],
[2, 2, 3, 1],
[2, 3, 1, 2],
[3, 1, 3, 2],
[3, 2, 1, 3],
[3, 3, 2, 1]
]
}
def select_orthogonal_array(self, factors, levels):
"""选择合适的正交表"""
max_levels = max(levels)
factor_count = len(factors)
if max_levels == 2:
if factor_count <= 3:
return "L4(2^3)", self.orthogonal_arrays["L4(2^3)"]
elif factor_count <= 7:
return "L8(2^7)", self.orthogonal_arrays["L8(2^7)"]
elif max_levels == 3:
if factor_count <= 4:
return "L9(3^4)", self.orthogonal_arrays["L9(3^4)"]
return None, None
实例1:浏览器兼容性测试
需求: 测试Web应用在不同浏览器、操作系统、分辨率组合下的兼容性
class BrowserCompatibilityTest:
"""浏览器兼容性正交测试"""
def __init__(self):
self.factors = {
"浏览器": ["Chrome", "Firefox", "Safari"],
"操作系统": ["Windows", "macOS", "Linux"],
"分辨率": ["1920x1080", "1366x768", "1280x720"],
"网络": ["4G", "WiFi", "有线"]
}
def design_orthogonal_test(self):
"""设计正交测试用例"""
# 使用L9(3^4)正交表,4个因子,每个因子3个水平
orthogonal_array = [
[1, 1, 1, 1],
[1, 2, 2, 2],
[1, 3, 3, 3],
[2, 1, 2, 3],
[2, 2, 3, 1],
[2, 3, 1, 2],
[3, 1, 3, 2],
[3, 2, 1, 3],
[3, 3, 2, 1]
]
factor_names = ["浏览器", "操作系统", "分辨率", "网络"]
test_cases = []
for i, combination in enumerate(orthogonal_array):
test_case = {
"case_id": f"OT{i+1:03d}",
"combination": {}
}
for j, level in enumerate(combination):
factor_name = factor_names[j]
factor_values = self.factors[factor_name]
test_case["combination"][factor_name] = factor_values[level-1]
test_cases.append(test_case)
return test_cases
def compare_with_full_combination(self):
"""与全组合测试对比"""
full_combinations = 1
for factor_values in self.factors.values():
full_combinations *= len(factor_values)
orthogonal_combinations = 9 # L9正交表
comparison = {
"全组合测试用例数": full_combinations,
"正交试验用例数": orthogonal_combinations,
"减少比例": f"{(1 - orthogonal_combinations/full_combinations)*100:.1f}%",
"效率提升": f"{full_combinations/orthogonal_combinations:.1f}倍"
}
return comparison
实例2:表单输入验证测试
class FormValidationOrthogonal:
"""表单验证正交测试"""
def __init__(self):
self.form_factors = {
"用户名": {
"levels": ["有效格式", "无效格式"],
"values": ["validuser", "@invalid"]
},
"邮箱": {
"levels": ["有效格式", "无效格式"],
"values": ["test@example.com", "invalid-email"]
},
"密码": {
"levels": ["符合规则", "不符合规则"],
"values": ["Password123!", "123"]
},
"年龄": {
"levels": ["有效范围", "无效范围"],
"values": ["25", "150"]
},
"手机号": {
"levels": ["有效格式", "无效格式"],
"values": ["13812345678", "123"]
}
}
def design_form_test_cases(self):
"""设计表单验证测试用例"""
# 使用L8(2^7)正交表,5个因子使用前5列
orthogonal_array = [
[1, 1, 1, 1, 1],
[1, 1, 1, 2, 2],
[1, 2, 2, 1, 1],
[1, 2, 2, 2, 2],
[2, 1, 2, 1, 2],
[2, 1, 2, 2, 1],
[2, 2, 1, 1, 2],
[2, 2, 1, 2, 1]
]
factor_names = ["用户名", "邮箱", "密码", "年龄", "手机号"]
test_cases = []
for i, combination in enumerate(orthogonal_array):
test_case = {
"case_id": f"FORM{i+1:03d}",
"inputs": {},
"expected_result": "成功" if all(level == 1 for level in combination) else "失败"
}
invalid_fields = []
for j, level in enumerate(combination):
factor_name = factor_names[j]
factor_info = self.form_factors[factor_name]
test_case["inputs"][factor_name] = factor_info["values"][level-1]
if level == 2: # 无效值
invalid_fields.append(factor_name)
if invalid_fields:
test_case["expected_errors"] = invalid_fields
test_cases.append(test_case)
return test_cases
def analyze_coverage(self):
"""分析覆盖率"""
total_combinations = 2 ** len(self.form_factors) # 2^5 = 32
orthogonal_combinations = 8
analysis = {
"因子数": len(self.form_factors),
"每个因子的水平数": 2,
"全组合数": total_combinations,
"正交表组合数": orthogonal_combinations,
"覆盖效率": f"{orthogonal_combinations/total_combinations*100:.1f}%",
"优势": [
"大幅减少测试用例数量",
"保证因子间的均匀搭配",
"发现因子交互作用的缺陷",
"适合多因子多水平测试"
]
}
return analysis
正交试验设计原则
def orthogonal_design_principles():
"""正交试验设计原则"""
principles = {
"均匀分散性": {
"描述": "试验点在试验范围内均匀分散",
"目的": "每个因子的每个水平出现次数相等"
},
"整齐可比性": {
"描述": "每个因子的每个水平与其他因子搭配相同次数",
"目的": "保证各因子间的平衡性"
},
"独立性": {
"描述": "各因子效应可以独立估计",
"目的": "便于分析各因子的影响"
}
}
# 正交表选择指南
selection_guide = {
"因子数≤水平数-1": "可直接使用对应正交表",
"因子数>水平数-1": "需要选择更大的正交表或降维处理",
"混合水平": "使用混合水平正交表",
"交互作用": "考虑因子间交互,选择更大正交表"
}
return principles, selection_guide
def orthogonal_test_steps():
"""正交试验测试步骤"""
steps = [
"1. 确定测试因子和水平",
"2. 选择合适的正交表",
"3. 将因子和水平分配到正交表",
"4. 根据正交表生成测试用例",
"5. 执行测试用例",
"6. 分析测试结果",
"7. 识别关键因子和最优组合"
]
return steps
场景法和流程分析
场景法是通过运用场景来对系统的功能点或业务流程进行描述,从而提高测试效果的一种方法。它强调从用户的角度出发,模拟真实的使用场景。
场景设计要素
class ScenarioTesting:
"""场景测试设计"""
def __init__(self):
self.scenario_elements = {
"参与者": "执行场景的用户或系统",
"前置条件": "场景开始前必须满足的条件",
"触发事件": "启动场景的事件或动作",
"基本流程": "正常情况下的操作步骤",
"分支流程": "异常或特殊情况的处理流程",
"后置条件": "场景结束后系统应达到的状态"
}
def scenario_types(self):
"""场景类型分类"""
types = {
"主成功场景": "最常见、最重要的用户操作流程",
"扩展场景": "包含分支和异常处理的复杂流程",
"异常场景": "错误处理和恢复场景",
"边界场景": "极限条件下的场景",
"集成场景": "多个系统或模块交互的场景"
}
return types
实例1:电商购物场景测试
class EcommerceScenarioTest:
"""电商购物场景测试"""
def __init__(self):
self.user_personas = {
"新用户": {"experience": "初次使用", "tech_savvy": "低"},
"老用户": {"experience": "经常使用", "tech_savvy": "高"},
"VIP用户": {"experience": "高频使用", "tech_savvy": "中"}
}
def design_purchase_scenarios(self):
"""设计购买场景"""
scenarios = [
{
"scenario_id": "SC001",
"name": "新用户首次购买",
"actor": "新用户",
"precondition": "用户未注册,商品有库存",
"trigger": "用户浏览商品页面",
"main_flow": [
"1. 用户浏览商品",
"2. 点击立即购买",
"3. 系统提示需要注册/登录",
"4. 用户选择注册",
"5. 填写注册信息",
"6. 验证邮箱/手机号",
"7. 注册成功自动登录",
"8. 确认商品信息",
"9. 填写收货地址",
"10. 选择支付方式",
"11. 完成支付",
"12. 显示订单确认页"
],
"alternate_flows": [
"3a. 用户选择登录现有账户",
"5a. 注册信息填写错误,系统提示重新填写",
"11a. 支付失败,提示重新支付"
],
"postcondition": "订单创建成功,用户收到确认信息"
},
{
"scenario_id": "SC002",
"name": "老用户批量购买",
"actor": "老用户",
"precondition": "用户已登录,购物车有商品",
"trigger": "用户进入购物车页面",
"main_flow": [
"1. 查看购物车商品列表",
"2. 修改商品数量",
"3. 删除不需要的商品",
"4. 选择优惠券",
"5. 确认订单信息",
"6. 选择配送地址",
"7. 选择配送方式",
"8. 确认支付信息",
"9. 完成支付",
"10. 订单提交成功"
],
"alternate_flows": [
"2a. 商品库存不足,系统提示并更新可购买数量",
"5a. 优惠券不可用,系统提示原因",
"9a. 支付失败,保存订单状态待支付"
],
"postcondition": "批量订单创建成功,库存更新"
},
{
"scenario_id": "SC003",
"name": "VIP用户预售抢购",
"actor": "VIP用户",
"precondition": "VIP用户已登录,预售活动进行中",
"trigger": "预售开始时间到达",
"main_flow": [
"1. 进入预售商品页面",
"2. 系统验证VIP资格",
"3. 显示VIP专享价格",
"4. 用户快速下单",
"5. 系统锁定库存",
"6. 使用VIP默认地址",
"7. 应用VIP优惠",
"8. 完成支付",
"9. 获得预售成功确认"
],
"alternate_flows": [
"5a. 库存不足,加入等待队列",
"8a. 支付超时,释放库存给下一位用户"
],
"postcondition": "VIP用户成功购买预售商品"
}
]
return scenarios
def generate_scenario_test_cases(self, scenario):
"""基于场景生成测试用例"""
test_cases = []
# 主流程测试用例
main_case = {
"case_id": f"{scenario['scenario_id']}_MAIN",
"scenario": scenario['name'],
"type": "主流程",
"steps": scenario['main_flow'],
"expected": scenario['postcondition']
}
test_cases.append(main_case)
# 分支流程测试用例
for i, alt_flow in enumerate(scenario['alternate_flows']):
alt_case = {
"case_id": f"{scenario['scenario_id']}_ALT_{i+1}",
"scenario": scenario['name'],
"type": "分支流程",
"description": alt_flow,
"expected": "系统正确处理异常情况"
}
test_cases.append(alt_case)
return test_cases
实例2:用户权限管理流程
class UserPermissionFlow:
"""用户权限管理流程测试"""
def __init__(self):
self.roles = {
"普通用户": ["查看", "编辑自己的数据"],
"部门管理员": ["查看", "编辑", "删除部门数据", "管理部门用户"],
"系统管理员": ["全部权限"]
}
def design_permission_scenarios(self):
"""设计权限管理场景"""
scenarios = [
{
"scenario_id": "PERM001",
"name": "权限升级流程",
"description": "普通用户申请成为部门管理员",
"flow_steps": [
{
"step": 1,
"actor": "普通用户",
"action": "提交权限申请",
"system_response": "申请状态变更为待审核",
"verification": "用户可以查看申请状态"
},
{
"step": 2,
"actor": "部门管理员",
"action": "审核权限申请",
"system_response": "收到审核通知",
"verification": "管理员可以看到待审核申请列表"
},
{
"step": 3,
"actor": "部门管理员",
"action": "批准申请",
"system_response": "用户权限升级",
"verification": "用户角色变更为部门管理员"
},
{
"step": 4,
"actor": "系统",
"action": "发送通知",
"system_response": "通知用户权限变更",
"verification": "用户收到权限升级通知"
}
],
"alternative_flows": [
{
"condition": "申请被拒绝",
"steps": "3a. 拒绝申请 → 通知用户 → 申请状态变更为已拒绝"
},
{
"condition": "申请超时",
"steps": "系统自动拒绝 → 通知申请人和审核人"
}
]
},
{
"scenario_id": "PERM002",
"name": "权限冲突处理",
"description": "用户同时属于多个角色时的权限处理",
"flow_steps": [
{
"step": 1,
"actor": "系统管理员",
"action": "为用户分配多个角色",
"system_response": "检查角色冲突",
"verification": "系统识别权限冲突"
},
{
"step": 2,
"actor": "系统",
"action": "应用权限合并规则",
"system_response": "按最高权限原则合并",
"verification": "用户获得所有角色的权限并集"
},
{
"step": 3,
"actor": "用户",
"action": "访问受限资源",
"system_response": "基于合并后权限验证",
"verification": "可以访问所有有权限的资源"
}
]
}
]
return scenarios
def create_flow_test_matrix(self):
"""创建流程测试矩阵"""
test_matrix = {
"角色组合": [
"普通用户 → 部门管理员",
"部门管理员 → 系统管理员",
"普通用户 → 系统管理员",
"多角色并存"
],
"操作类型": [
"权限申请",
"权限审批",
"权限撤销",
"权限查询"
],
"异常情况": [
"申请被拒绝",
"审核超时",
"权限冲突",
"系统异常"
]
}
# 生成测试用例组合
test_combinations = []
for role in test_matrix["角色组合"]:
for operation in test_matrix["操作类型"]:
for exception in test_matrix["异常情况"]:
test_combinations.append({
"role_change": role,
"operation": operation,
"exception": exception,
"test_focus": f"验证{role}执行{operation}时{exception}的处理"
})
return test_combinations
业务流程测试设计
class BusinessFlowTesting:
"""业务流程测试设计"""
def __init__(self):
self.flow_patterns = {
"顺序流程": "步骤按固定顺序执行",
"并行流程": "多个步骤可以同时执行",
"条件分支": "根据条件选择不同路径",
"循环流程": "某些步骤可能重复执行",
"异常处理": "错误发生时的处理流程"
}
def analyze_business_flow(self, flow_description):
"""分析业务流程"""
analysis = {
"关键路径": "识别最重要的业务路径",
"分支点": "找出所有的决策点",
"异常路径": "识别可能的异常情况",
"集成点": "确定与其他系统的集成点",
"检查点": "设置验证点确保流程正确性"
}
return analysis
def design_end_to_end_scenarios(self):
"""设计端到端场景"""
e2e_scenarios = [
{
"scenario": "完整订单处理流程",
"start_point": "客户下单",
"end_point": "订单完成",
"involved_systems": ["电商平台", "支付系统", "库存系统", "物流系统"],
"key_checkpoints": [
"订单创建成功",
"支付完成",
"库存扣减",
"物流发货",
"用户确认收货"
],
"rollback_scenarios": [
"支付失败回滚",
"库存不足处理",
"物流异常处理"
]
},
{
"scenario": "用户生命周期管理",
"start_point": "用户注册",
"end_point": "用户注销",
"flow_stages": [
"注册激活",
"首次登录",
"个人信息完善",
"业务操作",
"权限变更",
"账户暂停/恢复",
"账户注销"
],
"test_focus": [
"数据一致性",
"状态转换",
"权限控制",
"审计日志"
]
}
]
return e2e_scenarios
场景测试最佳实践
def scenario_testing_best_practices():
"""场景测试最佳实践"""
practices = {
"用户视角": {
"描述": "从真实用户角度设计场景",
"实施": [
"基于用户画像设计不同场景",
"考虑用户的知识水平和使用习惯",
"模拟真实的使用环境"
]
},
"完整性": {
"描述": "覆盖完整的业务流程",
"实施": [
"包含前置条件和后置条件",
"考虑正常流程和异常流程",
"验证数据的完整传递"
]
},
"可追溯性": {
"描述": "场景与需求的对应关系清晰",
"实施": [
"每个场景对应明确的业务需求",
"维护需求-场景-用例的追溯关系",
"及时更新场景以反映需求变化"
]
},
"可执行性": {
"描述": "场景能够被自动化或手工执行",
"实施": [
"步骤描述清晰具体",
"测试数据准备充分",
"期望结果明确可验证"
]
}
}
# 场景优先级评估
priority_criteria = {
"业务重要性": "核心业务流程优先级最高",
"使用频率": "高频使用场景优先级高",
"风险等级": "高风险场景需要重点测试",
"复杂度": "复杂场景容易出错,需要优先验证"
}
return practices, priority_criteria
实战:复杂业务场景的测试用例设计
案例背景:银行转账系统
银行转账系统来演示综合运用多种测试设计方法。
class BankTransferSystem:
"""银行转账系统测试用例设计"""
def __init__(self):
self.business_rules = {
"账户类型": ["储蓄账户", "支票账户", "信用账户"],
"转账限额": {
"单笔": {"普通用户": 50000, "VIP用户": 200000},
"日累计": {"普通用户": 200000, "VIP用户": 1000000}
},
"手续费": {
"同行": 0,
"跨行": {"<=10000": 5, ">10000": 10}
},
"验证方式": ["密码", "短信验证码", "硬件令牌"]
}
self.system_constraints = {
"营业时间": "07:00-22:00",
"维护时间": "02:00-06:00",
"最小转账金额": 1,
"最大转账金额": 1000000,
"账户余额精度": 2 # 小数点后2位
}
1. 等价类划分应用
def design_equivalence_class_tests(self):
"""应用等价类划分法设计转账金额测试"""
# 转账金额等价类划分
amount_equivalence_classes = {
"有效等价类": {
"EC1": "1 ≤ 金额 ≤ 50000 (普通用户单笔限额内)",
"EC2": "50001 ≤ 金额 ≤ 200000 (VIP用户单笔限额内)",
"EC3": "200001 ≤ 金额 ≤ 1000000 (VIP超额需特殊审批)"
},
"无效等价类": {
"EC4": "金额 < 1 (低于最小限额)",
"EC5": "金额 > 1000000 (超过系统限制)",
"EC6": "非数字字符",
"EC7": "小数点后超过2位",
"EC8": "负数",
"EC9": "空值"
}
}
# 基于等价类的测试用例
test_cases = [
# 有效等价类
{"id": "EC_001", "amount": 25000, "user_type": "普通", "expected": "成功"},
{"id": "EC_002", "amount": 100000, "user_type": "VIP", "expected": "成功"},
{"id": "EC_003", "amount": 500000, "user_type": "VIP", "expected": "需要审批"},
# 无效等价类
{"id": "EC_004", "amount": 0.5, "user_type": "普通", "expected": "金额过小"},
{"id": "EC_005", "amount": 1500000, "user_type": "VIP", "expected": "超出限制"},
{"id": "EC_006", "amount": "abc", "user_type": "普通", "expected": "格式错误"},
{"id": "EC_007", "amount": 100.123, "user_type": "普通", "expected": "精度错误"},
{"id": "EC_008", "amount": -1000, "user_type": "普通", "expected": "金额为负"},
{"id": "EC_009", "amount": None, "user_type": "普通", "expected": "金额为空"}
]
return test_cases
2. 边界值分析应用
def design_boundary_value_tests(self):
"""应用边界值分析法"""
# 转账金额边界值
amount_boundaries = [
{"value": 0, "description": "最小值-1", "expected": "失败"},
{"value": 1, "description": "最小值", "expected": "成功"},
{"value": 2, "description": "最小值+1", "expected": "成功"},
{"value": 49999, "description": "普通用户限额-1", "expected": "成功"},
{"value": 50000, "description": "普通用户限额", "expected": "成功"},
{"value": 50001, "description": "普通用户限额+1", "expected": "超限"},
{"value": 199999, "description": "VIP限额-1", "expected": "成功"},
{"value": 200000, "description": "VIP限额", "expected": "成功"},
{"value": 200001, "description": "VIP限额+1", "expected": "需审批"},
{"value": 999999, "description": "系统限额-1", "expected": "成功"},
{"value": 1000000, "description": "系统限额", "expected": "成功"},
{"value": 1000001, "description": "系统限额+1", "expected": "失败"}
]
# 时间边界值
time_boundaries = [
{"time": "06:59:59", "expected": "系统维护中"},
{"time": "07:00:00", "expected": "可以转账"},
{"time": "07:00:01", "expected": "可以转账"},
{"time": "21:59:59", "expected": "可以转账"},
{"time": "22:00:00", "expected": "可以转账"},
{"time": "22:00:01", "expected": "超出营业时间"}
]
return amount_boundaries, time_boundaries
3. 判定表法应用
def design_decision_table_tests(self):
"""应用判定表法设计转账验证测试"""
# 转账验证判定表
decision_table = {
"条件": ["账户余额充足", "密码正确", "验证码正确", "在营业时间"],
"规则": [
# 规则1-8:所有可能的条件组合
{"余额": True, "密码": True, "验证码": True, "时间": True, "结果": "转账成功"},
{"余额": True, "密码": True, "验证码": True, "时间": False, "结果": "非营业时间"},
{"余额": True, "密码": True, "验证码": False, "时间": True, "结果": "验证码错误"},
{"余额": True, "密码": False, "验证码": True, "时间": True, "结果": "密码错误"},
{"余额": False, "密码": True, "验证码": True, "时间": True, "结果": "余额不足"},
{"余额": True, "密码": False, "验证码": False, "时间": True, "结果": "密码和验证码错误"},
{"余额": False, "密码": False, "验证码": True, "时间": True, "结果": "余额不足和密码错误"},
{"余额": False, "密码": True, "验证码": False, "时间": False, "结果": "多项验证失败"}
]
}
# 生成测试用例
test_cases = []
for i, rule in enumerate(decision_table["规则"]):
test_case = {
"case_id": f"DT_{i+1:03d}",
"balance": 100000 if rule["余额"] else 100,
"password": "correct" if rule["密码"] else "wrong",
"verification_code": "123456" if rule["验证码"] else "wrong",
"transfer_time": "10:00:00" if rule["时间"] else "23:00:00",
"transfer_amount": 50000,
"expected_result": rule["结果"]
}
test_cases.append(test_case)
return test_cases
4. 状态转换法应用
def design_state_transition_tests(self):
"""应用状态转换法设计转账状态测试"""
# 转账状态转换
states = {
"INIT": "转账初始化",
"VALIDATING": "验证中",
"APPROVED": "已批准",
"PROCESSING": "处理中",
"COMPLETED": "已完成",
"FAILED": "已失败",
"CANCELLED": "已取消"
}
transitions = [
("INIT", "submit", "VALIDATING", "提交转账申请"),
("VALIDATING", "pass", "APPROVED", "验证通过"),
("VALIDATING", "fail", "FAILED", "验证失败"),
("APPROVED", "process", "PROCESSING", "开始处理"),
("PROCESSING", "success", "COMPLETED", "处理成功"),
("PROCESSING", "error", "FAILED", "处理失败"),
("VALIDATING", "cancel", "CANCELLED", "用户取消"),
("APPROVED", "cancel", "CANCELLED", "用户取消"),
("PROCESSING", "timeout", "FAILED", "处理超时")
]
# 状态转换测试路径
test_paths = [
{
"path_id": "ST001",
"description": "成功转账路径",
"path": "INIT → VALIDATING → APPROVED → PROCESSING → COMPLETED"
},
{
"path_id": "ST002",
"description": "验证失败路径",
"path": "INIT → VALIDATING → FAILED"
},
{
"path_id": "ST003",
"description": "处理失败路径",
"path": "INIT → VALIDATING → APPROVED → PROCESSING → FAILED"
},
{
"path_id": "ST004",
"description": "用户取消路径",
"path": "INIT → VALIDATING → CANCELLED"
}
]
return test_paths
5. 正交试验法应用
def design_orthogonal_tests(self):
"""应用正交试验法设计跨行转账测试"""
# 转账参数因子
factors = {
"账户类型": ["储蓄", "支票", "信用"],
"用户等级": ["普通", "VIP", "白金"],
"转账类型": ["同行", "跨行本地", "跨行异地"],
"验证方式": ["密码", "短信", "硬件令牌"]
}
# 使用L9(3^4)正交表
orthogonal_array = [
[1, 1, 1, 1], # 储蓄+普通+同行+密码
[1, 2, 2, 2], # 储蓄+VIP+跨行本地+短信
[1, 3, 3, 3], # 储蓄+白金+跨行异地+硬件令牌
[2, 1, 2, 3], # 支票+普通+跨行本地+硬件令牌
[2, 2, 3, 1], # 支票+VIP+跨行异地+密码
[2, 3, 1, 2], # 支票+白金+同行+短信
[3, 1, 3, 2], # 信用+普通+跨行异地+短信
[3, 2, 1, 3], # 信用+VIP+同行+硬件令牌
[3, 3, 2, 1] # 信用+白金+跨行本地+密码
]
# 生成正交测试用例
test_cases = []
factor_values = [list(factors.values())[i] for i in range(4)]
for i, combination in enumerate(orthogonal_array):
test_case = {
"case_id": f"OT_{i+1:03d}",
"account_type": factor_values[0][combination[0]-1],
"user_level": factor_values[1][combination[1]-1],
"transfer_type": factor_values[2][combination[2]-1],
"auth_method": factor_values[3][combination[3]-1],
"amount": 10000, # 固定金额
"expected": "根据组合条件确定预期结果"
}
test_cases.append(test_case)
return test_cases
6. 场景法应用
def design_scenario_tests(self):
"""应用场景法设计端到端转账场景"""
scenarios = [
{
"scenario_id": "SCENE001",
"name": "普通用户同行转账",
"actor": "普通银行用户",
"precondition": "用户已登录,账户余额充足",
"main_flow": [
"1. 用户选择转账功能",
"2. 输入收款人账户信息",
"3. 系统验证收款账户有效性",
"4. 输入转账金额",
"5. 系统校验转账限额",
"6. 输入交易密码",
"7. 输入短信验证码",
"8. 系统验证所有信息",
"9. 扣减转出账户余额",
"10. 增加转入账户余额",
"11. 生成交易记录",
"12. 发送转账成功通知"
],
"alternative_flows": [
"3a. 收款账户无效 → 提示错误,返回步骤2",
"5a. 超出转账限额 → 提示错误,返回步骤4",
"6a. 交易密码错误 → 提示错误,3次后锁定账户",
"7a. 验证码错误 → 提示错误,重新发送验证码",
"9a. 余额不足 → 提示错误,终止交易"
],
"postcondition": "转账完成,双方账户余额更新,生成交易记录"
},
{
"scenario_id": "SCENE002",
"name": "VIP用户大额跨行转账",
"actor": "VIP银行用户",
"precondition": "VIP用户已登录,申请大额转账权限",
"main_flow": [
"1. VIP用户选择大额转账",
"2. 系统确认VIP权限",
"3. 输入跨行收款信息",
"4. 输入大额转账金额",
"5. 系统计算跨行手续费",
"6. 用户确认转账信息和费用",
"7. 进行多重身份验证",
"8. 提交转账申请到人工审核",
"9. 审核员审核转账申请",
"10. 审核通过后执行转账",
"11. 跨行清算处理",
"12. 转账完成通知"
],
"alternative_flows": [
"9a. 审核拒绝 → 通知用户,说明拒绝原因",
"11a. 跨行清算失败 → 回滚交易,通知用户"
],
"postcondition": "大额跨行转账完成,通过审核流程"
}
]
return scenarios
7. 综合测试策略
def create_comprehensive_test_strategy(self):
"""创建综合测试策略"""
test_strategy = {
"功能测试": {
"等价类划分": "覆盖不同类型的输入数据",
"边界值分析": "重点测试临界值和边界条件",
"判定表法": "验证复杂业务规则的正确实现"
},
"流程测试": {
"状态转换": "验证转账状态的正确转换",
"场景测试": "模拟真实用户操作场景"
},
"组合测试": {
"正交试验": "用最少用例覆盖最多参数组合",
"成对测试": "重点测试参数间的交互影响"
},
"非功能测试": {
"性能测试": "高并发转账场景的性能验证",
"安全测试": "转账过程的安全性验证",
"可靠性测试": "异常情况下的系统恢复能力"
}
}
# 测试优先级矩阵
priority_matrix = {
"高优先级": [
"基本转账功能(等价类+边界值)",
"核心业务流程(场景法)",
"安全验证(判定表法)"
],
"中优先级": [
"状态转换验证",
"参数组合测试(正交法)",
"异常处理流程"
],
"低优先级": [
"边缘场景测试",
"性能边界测试",
"兼容性测试"
]
}
return test_strategy, priority_matrix
def calculate_test_coverage(self):
"""计算测试覆盖率"""
coverage_metrics = {
"需求覆盖率": "测试用例覆盖的需求数量 / 总需求数量",
"功能点覆盖率": "测试的功能点数量 / 总功能点数量",
"路径覆盖率": "测试的执行路径数量 / 总路径数量",
"状态覆盖率": "测试的状态数量 / 总状态数量",
"条件组合覆盖率": "测试的条件组合数量 / 总条件组合数量"
}
# 示例覆盖率计算
actual_coverage = {
"需求覆盖率": "95%",
"功能点覆盖率": "90%",
"路径覆盖率": "85%",
"状态覆盖率": "100%",
"条件组合覆盖率": "80%"
}
return coverage_metrics, actual_coverage
测试用例设计最佳实践
1. 设计原则
def test_design_principles():
"""测试用例设计原则"""
principles = {
"完整性原则": {
"描述": "测试用例应该覆盖所有需求和功能点",
"实施方法": [
"基于需求分析设计测试用例",
"使用需求追溯矩阵确保覆盖",
"定期评审测试覆盖率"
]
},
"独立性原则": {
"描述": "测试用例之间应该相互独立",
"实施方法": [
"每个用例有独立的测试数据",
"避免用例间的执行依赖",
"确保用例可以单独执行"
]
},
"可重复性原则": {
"描述": "测试用例应该能够重复执行并得到一致结果",
"实施方法": [
"明确的前置条件设置",
"确定性的测试数据",
"清晰的执行步骤"
]
},
"经济性原则": {
"描述": "用最少的用例发现最多的缺陷",
"实施方法": [
"优先设计高风险功能的用例",
"使用等价类减少冗余用例",
"重点测试核心业务流程"
]
}
}
return principles
2. 质量评估
def evaluate_test_case_quality():
"""测试用例质量评估"""
quality_metrics = {
"设计质量": {
"指标": [
"需求覆盖率",
"代码覆盖率",
"分支覆盖率",
"路径覆盖率"
],
"评估标准": {
"优秀": ">= 90%",
"良好": "80% - 89%",
"一般": "70% - 79%",
"较差": "< 70%"
}
},
"执行质量": {
"指标": [
"用例通过率",
"缺陷发现率",
"误报率",
"执行效率"
],
"计算方法": {
"通过率": "通过用例数 / 总用例数",
"发现率": "发现缺陷数 / 总缺陷数",
"误报率": "误报数 / 总报告数"
}
},
"维护质量": {
"指标": [
"用例更新及时性",
"用例可读性",
"用例可维护性"
],
"评估方法": [
"定期审查用例有效性",
"跟踪用例修改频率",
"评估用例理解难度"
]
}
}
return quality_metrics
def create_test_case_review_checklist():
"""创建测试用例评审检查清单"""
checklist = {
"基本信息": [
"□ 用例编号唯一且符合命名规范",
"□ 用例标题简洁明确",
"□ 优先级设置合理",
"□ 设计者和日期信息完整"
],
"测试内容": [
"□ 前置条件描述清晰完整",
"□ 测试步骤详细具体",
"□ 测试数据准确有效",
"□ 预期结果明确可验证",
"□ 后置条件明确"
],
"设计方法": [
"□ 选择了合适的设计方法",
"□ 覆盖了正常流程",
"□ 包含了异常处理",
"□ 考虑了边界条件"
],
"可执行性": [
"□ 步骤描述准确无歧义",
"□ 测试环境要求明确",
"□ 测试数据容易准备",
"□ 结果验证方法可行"
]
}
return checklist
3. 自动化适配
def design_for_automation():
"""面向自动化的测试用例设计"""
automation_guidelines = {
"数据驱动设计": {
"原则": "将测试数据与测试逻辑分离",
"实施": [
"使用参数化测试用例",
"创建测试数据模板",
"支持数据文件导入"
],
"示例": """
@pytest.mark.parametrize("username,password,expected", [
("valid_user", "valid_pass", "success"),
("invalid_user", "valid_pass", "user_error"),
("valid_user", "invalid_pass", "password_error")
])
def test_login(username, password, expected):
result = login(username, password)
assert result == expected
"""
},
"页面对象模式": {
"原则": "将页面元素和操作封装",
"实施": [
"创建页面对象类",
"封装页面操作方法",
"维护元素定位器"
],
"示例": """
class LoginPage:
def __init__(self, driver):
self.driver = driver
def enter_username(self, username):
self.driver.find_element("id", "username").send_keys(username)
def enter_password(self, password):
self.driver.find_element("id", "password").send_keys(password)
def click_login(self):
self.driver.find_element("id", "login-btn").click()
"""
},
"关键字驱动": {
"原则": "使用关键字描述测试步骤",
"实施": [
"定义标准关键字库",
"创建关键字组合",
"支持自然语言描述"
]
}
}
return automation_guidelines
20万+

被折叠的 条评论
为什么被折叠?



