白盒测试(White Box Testing)

白盒测试(White Box Testing)

一句话总结:深入代码内部,测试所有代码路径和逻辑分支。

🌟 快速理解(小白入门)

用生活化类比

就像汽车维修工检查发动机

1
2
3
4
5
6
7
8
9
10
11
黑盒测试(用户):
- 踩油门 → 车加速 ✅
- 踩刹车 → 车减速 ✅
(不关心发动机如何工作)

白盒测试(维修工):
- 检查发动机每个零件 🔧
- 检查电路是否正常 ⚡
- 检查油路是否畅通 🛢️
- 检查所有可能的故障点 🔍
(深入内部,检查每个细节)

真实场景

测试登录功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 黑盒测试:只测试输入输出
def test_login_black_box():
assert login("test@example.com", "password123") == True

# 白盒测试:测试内部逻辑
def test_login_white_box():
# 测试密码加密逻辑
assert hash_password("password123") == "hashed_value"

# 测试数据库查询逻辑
assert db.query("SELECT * FROM users WHERE email='test@example.com'") is not None

# 测试 Session 创建逻辑
assert create_session(user_id=1) is not None

# 测试所有代码分支
# 分支 1:用户存在 + 密码正确
# 分支 2:用户存在 + 密码错误
# 分支 3:用户不存在

📌 核心概念

什么是白盒测试?

白盒测试(White Box Testing):深入代码内部,测试所有代码路径、逻辑分支和内部结构。

白盒测试 vs 黑盒测试

维度 白盒测试 黑盒测试
关注点 代码逻辑 功能
测试者 开发者 测试人员/用户
测试依据 代码结构 需求文档
覆盖率 代码覆盖率 功能覆盖率
优势 发现逻辑错误 模拟真实用户

🎯 白盒测试的优势

优势 1:高代码覆盖率 📊

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def calculate_discount(is_member, amount):
"""计算折扣"""
if is_member:
if amount >= 100:
return 0.9 # 分支 1
else:
return 0.95 # 分支 2
else:
if amount >= 100:
return 0.95 # 分支 3
else:
return 1.0 # 分支 4

# 白盒测试:覆盖所有分支
def test_calculate_discount_white_box():
# 分支 1:会员 + 满100元
assert calculate_discount(True, 100) == 0.9

# 分支 2:会员 + 不满100元
assert calculate_discount(True, 50) == 0.95

# 分支 3:非会员 + 满100元
assert calculate_discount(False, 100) == 0.95

# 分支 4:非会员 + 不满100元
assert calculate_discount(False, 50) == 1.0

# 代码覆盖率:100%

优势 2:发现隐藏缺陷 🔍

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def divide(a, b):
"""除法函数"""
return a / b # 潜在问题:b=0 时会报错

# 黑盒测试:可能遗漏边界情况
def test_divide_black_box():
assert divide(10, 2) == 5

# 白盒测试:发现潜在问题
def test_divide_white_box():
# 正常情况
assert divide(10, 2) == 5

# 边界情况:除数为0
with pytest.raises(ZeroDivisionError):
divide(10, 0)

# 边界情况:负数
assert divide(-10, 2) == -5

# 边界情况:小数
assert divide(10, 3) == pytest.approx(3.333, 0.001)

🔄 白盒测试技术

1. 语句覆盖

定义:每条语句至少执行一次。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
def check_age(age):
if age >= 18:
print("成年人") # 语句 1
return True
else:
print("未成年人") # 语句 2
return False

# 语句覆盖测试
def test_check_age_statement_coverage():
check_age(20) # 覆盖语句 1
check_age(15) # 覆盖语句 2
# 语句覆盖率:100%

2. 分支覆盖

定义:每个分支至少执行一次。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def validate_user(email, password):
if email and password: # 分支 1
if len(password) >= 6: # 分支 2
return True
else:
return False
else:
return False

# 分支覆盖测试
def test_validate_user_branch_coverage():
# 分支 1-True, 分支 2-True
assert validate_user("test@example.com", "password123") == True

# 分支 1-True, 分支 2-False
assert validate_user("test@example.com", "123") == False

# 分支 1-False
assert validate_user("", "password123") == False

# 分支覆盖率:100%

3. 路径覆盖

定义:所有可能的执行路径至少执行一次。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def process_order(is_member, amount, has_coupon):
discount = 1.0

if is_member: # 路径分叉点 1
discount = 0.95

if amount >= 100: # 路径分叉点 2
discount *= 0.9

if has_coupon: # 路径分叉点 3
discount *= 0.95

return amount * discount

# 路径覆盖测试(2^3 = 8 条路径)
@pytest.mark.parametrize("is_member,amount,has_coupon,expected", [
(True, 100, True, 100 * 0.95 * 0.9 * 0.95), # 路径 1
(True, 100, False, 100 * 0.95 * 0.9), # 路径 2
(True, 50, True, 50 * 0.95 * 0.95), # 路径 3
(True, 50, False, 50 * 0.95), # 路径 4
(False, 100, True, 100 * 0.9 * 0.95), # 路径 5
(False, 100, False, 100 * 0.9), # 路径 6
(False, 50, True, 50 * 0.95), # 路径 7
(False, 50, False, 50), # 路径 8
])
def test_process_order_path_coverage(is_member, amount, has_coupon, expected):
assert process_order(is_member, amount, has_coupon) == pytest.approx(expected)
# 路径覆盖率:100%

4. 条件覆盖

定义:每个条件的 True 和 False 都至少执行一次。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def can_vote(age, is_citizen):
if age >= 18 and is_citizen: # 条件 1: age >= 18, 条件 2: is_citizen
return True
return False

# 条件覆盖测试
def test_can_vote_condition_coverage():
# 条件 1-True, 条件 2-True
assert can_vote(20, True) == True

# 条件 1-True, 条件 2-False
assert can_vote(20, False) == False

# 条件 1-False, 条件 2-True
assert can_vote(15, True) == False

# 条件 1-False, 条件 2-False
assert can_vote(15, False) == False

# 条件覆盖率:100%

📊 最佳实践

1. 使用代码覆盖率工具 📊

1
2
3
4
5
6
7
8
9
10
11
# 使用 pytest-cov 测量代码覆盖率
pytest --cov=app --cov-report=html

# 输出:
# Name Stmts Miss Cover
# ---------------------------------------
# app/__init__.py 5 0 100%
# app/auth.py 20 2 90%
# app/order.py 30 5 83%
# ---------------------------------------
# TOTAL 55 7 87%

2. 目标覆盖率 🎯

1
2
3
4
5
6
# 设置最低覆盖率要求
# pytest.ini
[tool:pytest]
addopts = --cov=app --cov-fail-under=80

# 如果覆盖率低于 80%,测试失败

3. 测试边界条件 🔍

1
2
3
4
5
6
7
8
9
10
11
12
13
def test_boundary_conditions():
"""测试边界条件"""
# 最小值
assert validate_age(0) == False
assert validate_age(1) == False

# 边界值
assert validate_age(17) == False
assert validate_age(18) == True

# 最大值
assert validate_age(120) == True
assert validate_age(121) == False

🛠️ 白盒测试工具

代码覆盖率工具

工具 语言 特点 推荐度
pytest-cov Python 简单、强大 ⭐⭐⭐⭐⭐
Coverage.py Python 详细报告 ⭐⭐⭐⭐⭐
Istanbul JavaScript 标准工具 ⭐⭐⭐⭐⭐
JaCoCo Java 企业级 ⭐⭐⭐⭐

🎓 学习资源

书籍推荐

  • 《代码大全》- Steve McConnell
  • 《重构》- Martin Fowler

官方文档


🔗 相关主题

  • [[黑盒测试]] - 关注功能的测试
  • [[单元测试]] - 通常是白盒测试
  • [[代码覆盖率]] - 白盒测试的度量指标

💡 快速参考卡片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
白盒测试速查表
================

定义:深入代码内部,测试所有代码路径

核心特征:
🔍 关注代码逻辑
📊 高代码覆盖率
🐛 发现隐藏缺陷
👨‍💻 需要懂代码

测试技术:
1. 语句覆盖(每条语句执行一次)
2. 分支覆盖(每个分支执行一次)
3. 路径覆盖(所有路径执行一次)
4. 条件覆盖(所有条件执行一次)

白盒 vs 黑盒:
- 白盒:测试代码(开发者视角)
- 黑盒:测试功能(用户视角)

最佳实践:
1. 使用代码覆盖率工具
2. 目标覆盖率 80%+
3. 测试边界条件
4. 测试所有分支

推荐工具:
- Python: pytest-cov
- JavaScript: Istanbul
- Java: JaCoCo

适用场景:
✅ 单元测试
✅ 集成测试
✅ 代码审查
✅ 重构验证

覆盖率目标:
- 核心代码:90%+
- 一般代码:80%+
- 工具代码:70%+