软件工程:白盒测试

📌目录



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

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

🎯 一、白盒测试概述

(一)白盒测试定义

白盒测试(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
  • 基本路径测试:基于圈复杂度设计测试用例
  • 路径覆盖:覆盖所有可能路径

💡 应用场景

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

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


相关推荐
xierui1231234 小时前
Agent记忆不是聊天记录:用三层存储构建可追溯的 AI 工作流
数据库·人工智能·aigc·软件工程
帅次8 小时前
软考中项第11章:规划过程组,核心知识点与备考重点
项目管理·软件工程·软考·系统集成项目管理工程师·软考中项
码匠许师傅10 小时前
【设计模式精讲】21.迭代器模式(Iterator)
c++·设计模式·rpc·迭代器模式·软件工程·uml
梁辰兴10 小时前
软件工程:软件测试的基本步骤
软件测试·软件工程·环境搭建·测试设计·测试计划·梁辰兴·测试执行
梁辰兴11 小时前
软件工程:软件测试的目的和重要性
软件测试·软件工程·梁辰兴·软件测试原则·软件测试的重要性·软件测试的目的·软件测试类型
梁辰兴1 天前
软件工程:软件测试的特点和原则
软件测试·软件工程·实践应用·梁辰兴·软件测试特点·软件测试原则·测试缺陷
帅次1 天前
软考中项第5章:软件工程,核心知识点与备考重点
项目管理·软件工程·软考·系统集成项目管理工程师·软考中项
梁辰兴1 天前
软件工程:静态分析与动态测试
静态分析·软件工程·动态测试·梁辰兴·结合使用·选择策略·软件质量保证
陈彬深大1 天前
《AI 渐进编程》之二十九:AI 为什么要先重构软件工程
人工智能·软件工程