软件工程:白盒测试

📌目录



⚖️ 白盒测试:深入代码内部的测试方法

白盒测试是一种基于程序内部逻辑结构的测试方法,它要求测试人员了解程序的内部实现,通过检查代码的逻辑路径来设计测试用例。本文将详细介绍白盒测试的概念、方法、技术和应用。

🎯 一、白盒测试概述

(一)白盒测试定义

白盒测试(White Box Testing)又称结构测试、逻辑驱动测试,是一种基于程序内部逻辑结构的测试方法。

白盒测试特点:
#mermaid-svg-rcLHabWc5S43JYNg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rcLHabWc5S43JYNg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rcLHabWc5S43JYNg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rcLHabWc5S43JYNg .error-icon{fill:#552222;}#mermaid-svg-rcLHabWc5S43JYNg .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rcLHabWc5S43JYNg .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rcLHabWc5S43JYNg .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rcLHabWc5S43JYNg .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rcLHabWc5S43JYNg .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rcLHabWc5S43JYNg .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rcLHabWc5S43JYNg .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rcLHabWc5S43JYNg .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rcLHabWc5S43JYNg .marker.cross{stroke:#333333;}#mermaid-svg-rcLHabWc5S43JYNg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rcLHabWc5S43JYNg p{margin:0;}#mermaid-svg-rcLHabWc5S43JYNg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-rcLHabWc5S43JYNg .cluster-label text{fill:#333;}#mermaid-svg-rcLHabWc5S43JYNg .cluster-label span{color:#333;}#mermaid-svg-rcLHabWc5S43JYNg .cluster-label span p{background-color:transparent;}#mermaid-svg-rcLHabWc5S43JYNg .label text,#mermaid-svg-rcLHabWc5S43JYNg span{fill:#333;color:#333;}#mermaid-svg-rcLHabWc5S43JYNg .node rect,#mermaid-svg-rcLHabWc5S43JYNg .node circle,#mermaid-svg-rcLHabWc5S43JYNg .node ellipse,#mermaid-svg-rcLHabWc5S43JYNg .node polygon,#mermaid-svg-rcLHabWc5S43JYNg .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rcLHabWc5S43JYNg .rough-node .label text,#mermaid-svg-rcLHabWc5S43JYNg .node .label text,#mermaid-svg-rcLHabWc5S43JYNg .image-shape .label,#mermaid-svg-rcLHabWc5S43JYNg .icon-shape .label{text-anchor:middle;}#mermaid-svg-rcLHabWc5S43JYNg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-rcLHabWc5S43JYNg .rough-node .label,#mermaid-svg-rcLHabWc5S43JYNg .node .label,#mermaid-svg-rcLHabWc5S43JYNg .image-shape .label,#mermaid-svg-rcLHabWc5S43JYNg .icon-shape .label{text-align:center;}#mermaid-svg-rcLHabWc5S43JYNg .node.clickable{cursor:pointer;}#mermaid-svg-rcLHabWc5S43JYNg .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-rcLHabWc5S43JYNg .arrowheadPath{fill:#333333;}#mermaid-svg-rcLHabWc5S43JYNg .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-rcLHabWc5S43JYNg .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-rcLHabWc5S43JYNg .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rcLHabWc5S43JYNg .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-rcLHabWc5S43JYNg .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rcLHabWc5S43JYNg .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-rcLHabWc5S43JYNg .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-rcLHabWc5S43JYNg .cluster text{fill:#333;}#mermaid-svg-rcLHabWc5S43JYNg .cluster span{color:#333;}#mermaid-svg-rcLHabWc5S43JYNg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-rcLHabWc5S43JYNg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-rcLHabWc5S43JYNg rect.text{fill:none;stroke-width:0;}#mermaid-svg-rcLHabWc5S43JYNg .icon-shape,#mermaid-svg-rcLHabWc5S43JYNg .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rcLHabWc5S43JYNg .icon-shape p,#mermaid-svg-rcLHabWc5S43JYNg .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-rcLHabWc5S43JYNg .icon-shape .label rect,#mermaid-svg-rcLHabWc5S43JYNg .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rcLHabWc5S43JYNg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-rcLHabWc5S43JYNg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-rcLHabWc5S43JYNg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 白盒测试
了解内部结构
基于代码逻辑
检查路径覆盖
验证内部行为
查看源代码
理解控制流
分析判定条件
设计测试用例
语句覆盖
判定覆盖
条件覆盖
路径覆盖
验证逻辑正确性
发现内部缺陷

(二)白盒测试与黑盒测试对比

白盒测试与黑盒测试对比:

对比维度 白盒测试 黑盒测试
测试依据 内部逻辑结构 需求规格说明
测试视角 程序内部 程序外部
是否需要代码 需要 不需要
测试设计 基于代码逻辑 基于功能需求
覆盖标准 代码覆盖率 功能覆盖率
适用阶段 单元测试 系统测试、验收测试
测试人员 开发人员 测试人员、用户

📦 二、逻辑覆盖技术

(一)覆盖技术概述

逻辑覆盖技术是白盒测试的核心方法,用于衡量测试的完整性。

覆盖技术层次:

覆盖类型 覆盖强度 说明
语句覆盖 最弱 每条语句至少执行一次
判定覆盖 较弱 每个判定的真假都执行
条件覆盖 较弱 每个条件的真假都执行
判定/条件覆盖 中等 同时满足判定和条件覆盖
条件组合覆盖 较强 所有条件组合都执行
路径覆盖 最强 每条路径都执行

(二)语句覆盖

语句覆盖要求每条语句至少执行一次。

语句覆盖示例:

python 复制代码
# 语句覆盖示例

# 被测程序
def calculate_bonus(salary, performance):
    """计算奖金"""
    
    bonus = 0
    
    if performance > 80:
        bonus = salary * 0.2
    
    if performance > 90:
        bonus = salary * 0.3
    
    return bonus

# 语句覆盖分析
def statement_coverage():
    """语句覆盖分析"""
    
    print("语句覆盖分析")
    print("=" * 70)
    
    print("\n程序语句:")
    print("-" * 70)
    print("  S1: bonus = 0")
    print("  S2: if performance > 80:")
    print("  S3:     bonus = salary * 0.2")
    print("  S4: if performance > 90:")
    print("  S5:     bonus = salary * 0.3")
    print("  S6: return bonus")
    
    print("\n语句覆盖要求:")
    print("  每条语句至少执行一次")
    
    print("\n测试用例设计:")
    print("-" * 70)
    
    # 测试用例
    test_cases = [
        {
            "id": "TC1",
            "input": {"salary": 10000, "performance": 95},
            "execution": ["S1", "S2", "S3", "S4", "S5", "S6"],
            "output": 3000,
            "coverage": "覆盖所有语句"
        }
    ]
    
    for tc in test_cases:
        print(f"\n测试用例:{tc['id']}")
        print(f"  输入: salary={tc['input']['salary']}, performance={tc['input']['performance']}")
        print(f"  执行路径: {' → '.join(tc['execution'])}")
        print(f"  输出: {tc['output']}")
        print(f"  覆盖情况: {tc['coverage']}")
    
    print("\n语句覆盖分析:")
    print("-" * 70)
    print("  ✓ S1: bonus = 0 被执行")
    print("  ✓ S2: if performance > 80 被执行")
    print("  ✓ S3: bonus = salary * 0.2 被执行")
    print("  ✓ S4: if performance > 90 被执行")
    print("  ✓ S5: bonus = salary * 0.3 被执行")
    print("  ✓ S6: return bonus 被执行")
    print("\n  语句覆盖率: 100% (6/6)")
    
    print("\n语句覆盖的不足:")
    print("  - 不能发现判定条件的错误")
    print("  - 不能发现条件组合的错误")
    print("  - 测试强度较弱")

statement_coverage()

(三)判定覆盖

判定覆盖要求每个判定的真假分支都至少执行一次。

判定覆盖示例:

python 复制代码
# 判定覆盖示例

# 被测程序
def process_order(quantity, price):
    """处理订单"""
    
    if quantity > 0 and price > 0:
        total = quantity * price
        status = "有效"
    else:
        total = 0
        status = "无效"
    
    return {"total": total, "status": status}

# 判定覆盖分析
def decision_coverage():
    """判定覆盖分析"""
    
    print("判定覆盖分析")
    print("=" * 70)
    
    print("\n程序判定:")
    print("-" * 70)
    print("  D1: if quantity > 0 and price > 0")
    print("      真分支: total = quantity * price, status = '有效'")
    print("      假分支: total = 0, status = '无效'")
    
    print("\n判定覆盖要求:")
    print("  每个判定的真假分支都至少执行一次")
    
    print("\n测试用例设计:")
    print("-" * 70)
    
    test_cases = [
        {
            "id": "TC1",
            "input": {"quantity": 10, "price": 100},
            "decision": "D1为真",
            "output": {"total": 1000, "status": "有效"},
            "branch": "真分支"
        },
        {
            "id": "TC2",
            "input": {"quantity": 0, "price": 100},
            "decision": "D1为假",
            "output": {"total": 0, "status": "无效"},
            "branch": "假分支"
        }
    ]
    
    for tc in test_cases:
        print(f"\n测试用例:{tc['id']}")
        print(f"  输入: quantity={tc['input']['quantity']}, price={tc['input']['price']}")
        print(f"  判定结果: {tc['decision']}")
        print(f"  执行分支: {tc['branch']}")
        print(f"  输出: {tc['output']}")
    
    print("\n判定覆盖分析:")
    print("-" * 70)
    print("  判定D1:")
    print("    ✓ 真分支: TC1执行")
    print("    ✓ 假分支: TC2执行")
    print("\n  判定覆盖率: 100% (2/2)")
    
    print("\n判定覆盖的不足:")
    print("  - 不能保证条件覆盖")
    print("  - 例如:TC1中quantity>0为真,price>0也为真")
    print("  - 但quantity>0为假、price>0为真的情况未测试")

decision_coverage()

(四)条件覆盖

条件覆盖要求每个条件的所有可能取值都至少执行一次。

条件覆盖示例:

python 复制代码
# 条件覆盖示例

# 被测程序
def check_access(age, has_id):
    """检查访问权限"""
    
    if age >= 18 and has_id:
        access = "允许"
    else:
        access = "拒绝"
    
    return access

# 条件覆盖分析
def condition_coverage():
    """条件覆盖分析"""
    
    print("条件覆盖分析")
    print("=" * 70)
    
    print("\n程序条件:")
    print("-" * 70)
    print("  判定: if age >= 18 and has_id")
    print("  条件C1: age >= 18")
    print("  条件C2: has_id")
    
    print("\n条件覆盖要求:")
    print("  每个条件的所有可能取值都至少执行一次")
    
    print("\n条件取值:")
    print("-" * 70)
    print("  C1: age >= 18")
    print("      真: age = 20")
    print("      假: age = 16")
    print("  C2: has_id")
    print("      真: has_id = True")
    print("      假: has_id = False")
    
    print("\n测试用例设计:")
    print("-" * 70)
    
    test_cases = [
        {
            "id": "TC1",
            "input": {"age": 20, "has_id": True},
            "conditions": {"C1": "真", "C2": "真"},
            "output": "允许"
        },
        {
            "id": "TC2",
            "input": {"age": 16, "has_id": False},
            "conditions": {"C1": "假", "C2": "假"},
            "output": "拒绝"
        }
    ]
    
    for tc in test_cases:
        print(f"\n测试用例:{tc['id']}")
        print(f"  输入: age={tc['input']['age']}, has_id={tc['input']['has_id']}")
        print(f"  条件取值: C1={tc['conditions']['C1']}, C2={tc['conditions']['C2']}")
        print(f"  输出: {tc['output']}")
    
    print("\n条件覆盖分析:")
    print("-" * 70)
    print("  条件C1 (age >= 18):")
    print("    ✓ 真值: TC1执行")
    print("    ✓ 假值: TC2执行")
    print("  条件C2 (has_id):")
    print("    ✓ 真值: TC1执行")
    print("    ✓ 假值: TC2执行")
    print("\n  条件覆盖率: 100% (4/4)")
    
    print("\n条件覆盖的不足:")
    print("  - 不能保证判定覆盖")
    print("  - TC1: C1真, C2真 → 判定为真")
    print("  - TC2: C1假, C2假 → 判定为假")
    print("  - 但C1真C2假、C1假C2真的组合未测试")

condition_coverage()

(五)判定/条件覆盖

判定/条件覆盖要求同时满足判定覆盖和条件覆盖。

判定/条件覆盖示例:

python 复制代码
# 判定/条件覆盖示例

# 被测程序
def calculate_shipping(weight, is_express):
    """计算运费"""
    
    if weight > 10 or is_express:
        if weight > 20:
            shipping = 50
        else:
            shipping = 30
    else:
        shipping = 10
    
    return shipping

# 判定/条件覆盖分析
def decision_condition_coverage():
    """判定/条件覆盖分析"""
    
    print("判定/条件覆盖分析")
    print("=" * 70)
    
    print("\n程序判定和条件:")
    print("-" * 70)
    print("  判定D1: if weight > 10 or is_express")
    print("    条件C1: weight > 10")
    print("    条件C2: is_express")
    print("  判定D2: if weight > 20")
    print("    条件C3: weight > 20")
    
    print("\n判定/条件覆盖要求:")
    print("  同时满足判定覆盖和条件覆盖")
    
    print("\n测试用例设计:")
    print("-" * 70)
    
    test_cases = [
        {
            "id": "TC1",
            "input": {"weight": 25, "is_express": True},
            "conditions": {"C1": "真", "C2": "真", "C3": "真"},
            "decisions": {"D1": "真", "D2": "真"},
            "output": 50
        },
        {
            "id": "TC2",
            "input": {"weight": 5, "is_express": False},
            "conditions": {"C1": "假", "C2": "假", "C3": "假"},
            "decisions": {"D1": "假", "D2": "N/A"},
            "output": 10
        },
        {
            "id": "TC3",
            "input": {"weight": 15, "is_express": False},
            "conditions": {"C1": "真", "C2": "假", "C3": "假"},
            "decisions": {"D1": "真", "D2": "假"},
            "output": 30
        }
    ]
    
    for tc in test_cases:
        print(f"\n测试用例:{tc['id']}")
        print(f"  输入: weight={tc['input']['weight']}, is_express={tc['input']['is_express']}")
        print(f"  条件: C1={tc['conditions']['C1']}, C2={tc['conditions']['C2']}, C3={tc['conditions']['C3']}")
        print(f"  判定: D1={tc['decisions']['D1']}, D2={tc['decisions']['D2']}")
        print(f"  输出: {tc['output']}")
    
    print("\n覆盖分析:")
    print("-" * 70)
    print("  判定覆盖:")
    print("    ✓ D1真: TC1, TC3执行")
    print("    ✓ D1假: TC2执行")
    print("    ✓ D2真: TC1执行")
    print("    ✓ D2假: TC3执行")
    print("  条件覆盖:")
    print("    ✓ C1真: TC1, TC3执行")
    print("    ✓ C1假: TC2执行")
    print("    ✓ C2真: TC1执行")
    print("    ✓ C2假: TC2, TC3执行")
    print("    ✓ C3真: TC1执行")
    print("    ✓ C3假: TC2, TC3执行")
    print("\n  判定覆盖率: 100%")
    print("  条件覆盖率: 100%")

decision_condition_coverage()

(六)条件组合覆盖

条件组合覆盖要求所有条件的各种可能组合都至少执行一次。

条件组合覆盖示例:

python 复制代码
# 条件组合覆盖示例

# 被测程序
def validate_user(username, password, age):
    """验证用户"""
    
    if len(username) >= 6 and len(password) >= 8 and age >= 18:
        result = "有效用户"
    else:
        result = "无效用户"
    
    return result

# 条件组合覆盖分析
def multiple_condition_coverage():
    """条件组合覆盖分析"""
    
    print("条件组合覆盖分析")
    print("=" * 70)
    
    print("\n程序条件:")
    print("-" * 70)
    print("  判定: if len(username) >= 6 and len(password) >= 8 and age >= 18")
    print("  条件C1: len(username) >= 6")
    print("  条件C2: len(password) >= 8")
    print("  条件C3: age >= 18")
    
    print("\n条件组合覆盖要求:")
    print("  所有条件的各种可能组合都至少执行一次")
    
    print("\n条件组合数:")
    print("  3个条件,每个条件2个取值")
    print("  组合数 = 2^3 = 8种")
    
    print("\n条件组合列表:")
    print("-" * 70)
    
    combinations = [
        {"C1": "真", "C2": "真", "C3": "真", "username": "user01", "password": "password1", "age": 20},
        {"C1": "真", "C2": "真", "C3": "假", "username": "user01", "password": "password1", "age": 16},
        {"C1": "真", "C2": "假", "C3": "真", "username": "user01", "password": "pass1", "age": 20},
        {"C1": "真", "C2": "假", "C3": "假", "username": "user01", "password": "pass1", "age": 16},
        {"C1": "假", "C2": "真", "C3": "真", "username": "user", "password": "password1", "age": 20},
        {"C1": "假", "C2": "真", "C3": "假", "username": "user", "password": "password1", "age": 16},
        {"C1": "假", "C2": "假", "C3": "真", "username": "user", "password": "pass1", "age": 20},
        {"C1": "假", "C2": "假", "C3": "假", "username": "user", "password": "pass1", "age": 16},
    ]
    
    for i, combo in enumerate(combinations, 1):
        print(f"\n组合{i}:")
        print(f"  C1={combo['C1']}, C2={combo['C2']}, C3={combo['C3']}")
        print(f"  测试数据: username='{combo['username']}', password='{combo['password']}', age={combo['age']}")
    
    print("\n条件组合覆盖分析:")
    print("-" * 70)
    print(f"  总组合数: 8")
    print(f"  已覆盖: 8")
    print(f"  覆盖率: 100%")
    
    print("\n条件组合覆盖的特点:")
    print("  - 测试强度强")
    print("  - 可以发现条件组合的错误")
    print("  - 测试用例数量随条件数指数增长")
    print("  - 对于复杂程序,测试成本较高")

multiple_condition_coverage()

🌐 三、路径测试

(一)路径测试概述

路径测试要求覆盖程序中的所有可能路径。

路径测试概念:
渲染错误: Mermaid 渲染失败: Parse error on line 11: ... C --> C1V(G) = E - N + 2 ---------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PS'

(二)控制流图

控制流图是描述程序控制流程的图形表示。

控制流图示例:

python 复制代码
# 控制流图示例

# 被测程序
def process_data(x, y):
    """数据处理"""
    
    # 节点1
    result = 0
    
    # 节点2:判定
    if x > 0:
        # 节点3
        if y > 0:
            # 节点4
            result = x + y
        else:
            # 节点5
            result = x - y
    else:
        # 节点6
        if y > 0:
            # 节点7
            result = -x + y
        else:
            # 节点8
            result = -x - y
    
    # 节点9
    return result

# 控制流图分析
def control_flow_graph():
    """控制流图分析"""
    
    print("控制流图分析")
    print("=" * 70)
    
    print("\n程序控制流图:")
    print("-" * 70)
    print("""
         ┌─────────┐
         │  节点1  │  result = 0
         └────┬────┘
              │
         ┌────▼────┐
         │  节点2  │  if x > 0
         └────┬────┘
              │
        ┌─────┴─────┐
        │           │
   ┌────▼────┐ ┌────▼────┐
   │  节点3  │ │  节点6  │
   │ if y>0  │ │ if y>0  │
   └────┬────┘ └────┬────┘
        │           │
   ┌────┴────┐ ┌────┴────┐
   │         │ │         │
┌──▼──┐  ┌──▼──┐ ┌──▼──┐  ┌──▼──┐
│节点4│  │节点5│ │节点7│  │节点8│
│x+y  │  │x-y  │ │-x+y │  │-x-y │
└──┬──┘  └──┬──┘ └──┬──┘  └──┬──┘
   │        │       │        │
   └────┬───┴───────┴────────┘
        │
   ┌────▼────┐
   │  节点9  │  return result
   └─────────┘
    """)
    
    print("\n控制流图要素:")
    print("-" * 70)
    print("  节点(N): 9个")
    print("  边(E): 11条")
    print("  判定节点: 3个 (节点2, 3, 6)")
    
    print("\n圈复杂度计算:")
    print("-" * 70)
    print("  方法1: V(G) = E - N + 2")
    print("         V(G) = 11 - 9 + 2 = 4")
    print()
    print("  方法2: V(G) = 判定节点数 + 1")
    print("         V(G) = 3 + 1 = 4")
    print()
    print("  圈复杂度: 4")
    print("  含义: 需要至少4条基本路径")

control_flow_graph()

(三)基本路径测试

基本路径测试基于圈复杂度设计测试用例。

基本路径测试示例:

python 复制代码
# 基本路径测试示例

# 被测程序
def calculate_grade(score):
    """计算成绩等级"""
    
    if score >= 90:
        grade = "A"
    elif score >= 80:
        grade = "B"
    elif score >= 70:
        grade = "C"
    elif score >= 60:
        grade = "D"
    else:
        grade = "F"
    
    return grade

# 基本路径测试
def basic_path_testing():
    """基本路径测试"""
    
    print("基本路径测试")
    print("=" * 70)
    
    print("\n程序控制流图:")
    print("-" * 70)
    print("""
    节点1: score输入
        │
    节点2: if score >= 90
        │
   ┌────┴────┐
   │         │
节点3      节点4: elif score >= 80
grade=A       │
   │      ┌───┴───┐
   │      │       │
   │    节点5   节点6: elif score >= 70
   │    grade=B    │
   │      │    ┌───┴───┐
   │      │    │       │
   │      │  节点7   节点8: elif score >= 60
   │      │  grade=C    │
   │      │    │    ┌───┴───┐
   │      │    │    │       │
   │      │    │  节点9   节点10
   │      │    │  grade=D  grade=F
   │      │    │    │       │
   └──────┴────┴────┴───────┘
              │
           节点11: return grade
    """)
    
    print("\n圈复杂度计算:")
    print("-" * 70)
    print("  判定节点数: 4")
    print("  V(G) = 4 + 1 = 5")
    print("  需要5条基本路径")
    
    print("\n基本路径识别:")
    print("-" * 70)
    
    paths = [
        {
            "id": "路径1",
            "nodes": [1, 2, 3, 11],
            "condition": "score >= 90",
            "test_input": 95,
            "expected_output": "A"
        },
        {
            "id": "路径2",
            "nodes": [1, 2, 4, 5, 11],
            "condition": "80 <= score < 90",
            "test_input": 85,
            "expected_output": "B"
        },
        {
            "id": "路径3",
            "nodes": [1, 2, 4, 6, 7, 11],
            "condition": "70 <= score < 80",
            "test_input": 75,
            "expected_output": "C"
        },
        {
            "id": "路径4",
            "nodes": [1, 2, 4, 6, 8, 9, 11],
            "condition": "60 <= score < 70",
            "test_input": 65,
            "expected_output": "D"
        },
        {
            "id": "路径5",
            "nodes": [1, 2, 4, 6, 8, 10, 11],
            "condition": "score < 60",
            "test_input": 50,
            "expected_output": "F"
        }
    ]
    
    for path in paths:
        print(f"\n{path['id']}:")
        print(f"  节点序列: {' → '.join(map(str, path['nodes']))}")
        print(f"  条件: {path['condition']}")
        print(f"  测试输入: {path['test_input']}")
        print(f"  预期输出: {path['expected_output']}")
    
    print("\n测试执行:")
    print("-" * 70)
    
    for path in paths:
        result = calculate_grade(path['test_input'])
        status = "通过" if result == path['expected_output'] else "失败"
        print(f"  {path['id']}: score={path['test_input']} → grade={result} [{status}]")
    
    print("\n基本路径测试总结:")
    print("-" * 70)
    print(f"  圈复杂度: 5")
    print(f"  基本路径数: 5")
    print(f"  测试用例数: 5")
    print(f"  路径覆盖率: 100%")

basic_path_testing()

(四)路径覆盖

路径覆盖要求覆盖程序中的所有可能路径。

路径覆盖示例:

python 复制代码
# 路径覆盖示例

# 被测程序
def complex_function(a, b):
    """复杂函数"""
    
    result = 0
    
    if a > 0:
        if b > 0:
            result = 1  # 路径1
        else:
            result = 2  # 路径2
    else:
        if b > 0:
            result = 3  # 路径3
        else:
            result = 4  # 路径4
    
    return result

# 路径覆盖分析
def path_coverage():
    """路径覆盖分析"""
    
    print("路径覆盖分析")
    print("=" * 70)
    
    print("\n程序路径分析:")
    print("-" * 70)
    print("""
    路径1: a>0, b>0 → result=1
    路径2: a>0, b<=0 → result=2
    路径3: a<=0, b>0 → result=3
    路径4: a<=0, b<=0 → result=4
    
    总路径数: 4条
    """)
    
    print("\n路径覆盖要求:")
    print("  覆盖所有可能的路径")
    
    print("\n测试用例设计:")
    print("-" * 70)
    
    test_cases = [
        {
            "id": "TC1",
            "input": {"a": 5, "b": 3},
            "path": "路径1",
            "conditions": "a>0, b>0",
            "expected": 1
        },
        {
            "id": "TC2",
            "input": {"a": 5, "b": -3},
            "path": "路径2",
            "conditions": "a>0, b<=0",
            "expected": 2
        },
        {
            "id": "TC3",
            "input": {"a": -5, "b": 3},
            "path": "路径3",
            "conditions": "a<=0, b>0",
            "expected": 3
        },
        {
            "id": "TC4",
            "input": {"a": -5, "b": -3},
            "path": "路径4",
            "conditions": "a<=0, b<=0",
            "expected": 4
        }
    ]
    
    for tc in test_cases:
        print(f"\n测试用例:{tc['id']}")
        print(f"  输入: a={tc['input']['a']}, b={tc['input']['b']}")
        print(f"  覆盖路径: {tc['path']}")
        print(f"  条件: {tc['conditions']}")
        print(f"  预期输出: {tc['expected']}")
    
    print("\n测试执行:")
    print("-" * 70)
    
    for tc in test_cases:
        result = complex_function(tc['input']['a'], tc['input']['b'])
        status = "通过" if result == tc['expected'] else "失败"
        print(f"  {tc['id']}: a={tc['input']['a']}, b={tc['input']['b']} → {result} [{status}]")
    
    print("\n路径覆盖总结:")
    print("-" * 70)
    print(f"  总路径数: 4")
    print(f"  已覆盖: 4")
    print(f"  路径覆盖率: 100%")
    
    print("\n路径覆盖的特点:")
    print("  - 最强的覆盖标准")
    print("  - 可以发现所有路径上的错误")
    print("  - 对于复杂程序,路径数量可能非常大")
    print("  - 实际应用中通常使用基本路径测试")

path_coverage()

💡 四、白盒测试应用

(一)应用场景

白盒测试应用场景:

场景 说明
单元测试 测试单个函数或方法
关键代码 测试安全、金融等关键代码
代码审查 辅助代码审查过程
重构验证 验证代码重构的正确性
性能优化 分析代码性能瓶颈

(二)应用示例

白盒测试综合应用:

python 复制代码
# 白盒测试综合应用

# 被测程序:银行账户系统
class BankAccount:
    """银行账户"""
    
    def __init__(self, account_id, initial_balance=0):
        self.account_id = account_id
        self.balance = initial_balance
        self.transaction_history = []
    
    def deposit(self, amount):
        """存款"""
        
        if amount <= 0:
            return {"success": False, "message": "存款金额必须大于0"}
        
        if amount > 100000:
            return {"success": False, "message": "单笔存款不能超过10万"}
        
        self.balance += amount
        self.transaction_history.append({
            "type": "deposit",
            "amount": amount,
            "balance": self.balance
        })
        
        return {"success": True, "message": "存款成功", "balance": self.balance}
    
    def withdraw(self, amount):
        """取款"""
        
        if amount <= 0:
            return {"success": False, "message": "取款金额必须大于0"}
        
        if amount > self.balance:
            return {"success": False, "message": "余额不足"}
        
        if amount > 50000:
            return {"success": False, "message": "单笔取款不能超过5万"}
        
        self.balance -= amount
        self.transaction_history.append({
            "type": "withdraw",
            "amount": amount,
            "balance": self.balance
        })
        
        return {"success": False, "message": "取款成功", "balance": self.balance}

# 白盒测试
def white_box_testing_application():
    """白盒测试应用"""
    
    print("白盒测试综合应用")
    print("=" * 70)
    
    print("\n被测程序:BankAccount类")
    print("-" * 70)
    print("  方法1: deposit(amount)")
    print("  方法2: withdraw(amount)")
    
    print("\n\n1. deposit方法分析")
    print("=" * 70)
    
    print("\n控制流分析:")
    print("-" * 70)
    print("""
    路径1: amount <= 0 → 返回错误
    路径2: amount > 100000 → 返回错误
    路径3: 0 < amount <= 100000 → 存款成功
    
    判定节点: 2
    圈复杂度: 3
    """)
    
    print("\n测试用例设计(基本路径测试):")
    print("-" * 70)
    
    deposit_tests = [
        {"amount": -100, "path": "路径1", "expected": "错误"},
        {"amount": 200000, "path": "路径2", "expected": "错误"},
        {"amount": 1000, "path": "路径3", "expected": "成功"},
    ]
    
    account = BankAccount("ACC001", 10000)
    
    for i, test in enumerate(deposit_tests, 1):
        result = account.deposit(test["amount"])
        print(f"  TC{i}: amount={test['amount']}")
        print(f"    路径: {test['path']}")
        print(f"    结果: {result['message']}")
    
    print("\n\n2. withdraw方法分析")
    print("=" * 70)
    
    print("\n控制流分析:")
    print("-" * 70)
    print("""
    路径1: amount <= 0 → 返回错误
    路径2: amount > balance → 返回错误
    路径3: amount > 50000 → 返回错误
    路径4: 0 < amount <= min(balance, 50000) → 取款成功
    
    判定节点: 3
    圈复杂度: 4
    """)
    
    print("\n测试用例设计(基本路径测试):")
    print("-" * 70)
    
    withdraw_tests = [
        {"amount": -100, "path": "路径1", "expected": "错误"},
        {"amount": 100000, "path": "路径2", "expected": "错误"},
        {"amount": 60000, "path": "路径3", "expected": "错误"},
        {"amount": 1000, "path": "路径4", "expected": "成功"},
    ]
    
    account2 = BankAccount("ACC002", 10000)
    
    for i, test in enumerate(withdraw_tests, 1):
        result = account2.withdraw(test["amount"])
        print(f"  TC{i}: amount={test['amount']}")
        print(f"    路径: {test['path']}")
        print(f"    结果: {result['message']}")
    
    print("\n\n3. 测试覆盖率统计")
    print("=" * 70)
    
    coverage = {
        "deposit方法": {
            "语句覆盖": "100%",
            "判定覆盖": "100%",
            "条件覆盖": "100%",
            "路径覆盖": "100%"
        },
        "withdraw方法": {
            "语句覆盖": "100%",
            "判定覆盖": "100%",
            "条件覆盖": "100%",
            "路径覆盖": "100%"
        }
    }
    
    for method, rates in coverage.items():
        print(f"\n{method}:")
        for coverage_type, rate in rates.items():
            print(f"  {coverage_type}: {rate}")
    
    print("\n\n4. 发现的问题")
    print("=" * 70)
    
    issues = [
        {
            "method": "withdraw",
            "line": "return语句",
            "issue": "取款成功时返回success=False",
            "severity": "严重",
            "suggestion": "修改为return {'success': True, ...}"
        }
    ]
    
    for i, issue in enumerate(issues, 1):
        print(f"\n问题{i}:")
        print(f"  方法: {issue['method']}")
        print(f"  位置: {issue['line']}")
        print(f"  问题: {issue['issue']}")
        print(f"  严重程度: {issue['severity']}")
        print(f"  建议: {issue['suggestion']}")

white_box_testing_application()

📝 总结

白盒测试是一种基于程序内部逻辑结构的测试方法。

🎯 白盒测试概念:

  • 了解程序内部结构
  • 基于代码逻辑设计测试
  • 检查路径覆盖情况
  • 验证内部行为正确性

📦 逻辑覆盖技术:

  • 语句覆盖:每条语句至少执行一次(最弱)
  • 判定覆盖:每个判定的真假都执行
  • 条件覆盖:每个条件的真假都执行
  • 判定/条件覆盖:同时满足判定和条件覆盖
  • 条件组合覆盖:所有条件组合都执行
  • 路径覆盖:每条路径都执行(最强)

🌐 路径测试:

  • 控制流图:描述程序控制流程
  • 圈复杂度:V(G) = E - N + 2 = 判定节点数 + 1
  • 基本路径测试:基于圈复杂度设计测试用例
  • 路径覆盖:覆盖所有可能路径

💡 应用场景:

  • 单元测试:测试单个函数或方法
  • 关键代码:测试安全、金融等关键代码
  • 代码审查:辅助代码审查过程
  • 重构验证:验证代码重构的正确性

核心启示:白盒测试是深入代码内部的测试方法,可以发现黑盒测试难以发现的问题。在实际工作中,我们需要注意:第一,了解各种覆盖标准的强度和适用场景;第二,根据代码复杂度选择合适的覆盖标准;第三,使用工具辅助计算圈复杂度和生成测试用例;第四,结合黑盒测试,全面保证软件质量;第五,白盒测试成本较高,应重点用于关键代码。记住:白盒测试是保证代码质量的重要手段,但需要权衡测试成本和收益。


相关推荐
盖伦发发5 天前
软件工程SOLID 五大设计原则
后端·软件工程
Old Uncle Tom6 天前
评测即生死:Agent 时代的可靠性重构
人工智能·软件工程·agent
建筑工程企业管理系统6 天前
工程建设管理信息系统如何选型?多项目协同管控避坑指南
大数据·软件工程·软件需求
黄昏回响6 天前
软件工程基础知识(五):软件测试详解(下篇)——现代测试实践与论文指导
网络·其他·软件工程·改行学it
梁辰兴8 天前
软件工程:McCall软件质量因素
软件工程·梁辰兴·mccall软件质量因素·mccall模型·产品运行·产品修正·产品转移
爱码小白10 天前
开发在线部署工具,读懂组织管理:从代码架构悟创业底层逻辑
软件工程
梁辰兴10 天前
软件工程:软件维护的副作用
数据库·软件工程·梁辰兴·控制方法·软件维护的副作用·副作用类型·副作用原因
盖伦发发10 天前
业务开发常用设计模式:状态、策略、工厂、适配器、模板方法
后端·软件工程
梁辰兴11 天前
软件工程:逆向工程
软件工程·逆向工程·应用场景·梁辰兴·抽象层次·工程过程·方法工具
記億揺晃着的那天11 天前
Amazon Ads API 实战:如何高精度关联广告活动(Campaign)与 ASIN 及 ERP 产品主数据
软件工程·amazon·架构设计·系统设计·亚马逊·sp-api·amazon ads api