软件质量工程 06

软件测试 (Software Testing)

软件测试概述

  • 定义: 软件测试是评估软件质量的重要手段,通过验证软件是否满足指定要求并检测错误。

  • 目标: 通过系统地在受控环境下执行软件,确认软件质量,发现尚未发现的错误 (Good test focuses on finding as-yet-undiscovered errors, successful test uncovers unknown errors)。

  • 重要性:

    • 软件测试通常占开发工作量的 40% 至 50%,对于需要高可靠性的软件尤为重要。
    • 测试涵盖的方面包括:
      • 性能 (Performance)
      • 安全性 (Security)
      • 可扩展性 (Scalability)
      • 合规性 (Compliance)
      • 互操作性 (Interoperability)
      • 错误处理 (Error Handling)
  • 随着软件维护和扩展,测试时间需求增加,需要系统化的测试方法。

软件测试测量

  • 测量依据: 根据软件的属性或产品特性(如功能性、可靠性、可用性、效率、可维护性、安全性、可移植性等)进行测量。
  • 测试结果的四个层次:
    1. 是否运行了计划的测试?(Level 1: Did you run the planned tests?)
    2. 测试结果如何?(Level 2: What are the test results?)
    3. 是否应用测试结果改进代码?(Level 3: Did you apply the test results to improve the code?)
    4. 测试投入的回报率如何?(Level 4: What is the return on investment for the amount spent on testing?)

常用软件测试指标 (Common Software Testing Metrics)

指标名称 (Metric Name) 描述 (Description)
测试用例通过率 (Test Case Pass Rate) 执行测试用例中通过的百分比 (Percentage of passed test cases over total executed)
缺陷密度 (Defect Density) 每千行代码的确认缺陷数量 (Number of confirmed defects per KLOC)
测试覆盖率 (Test Coverage) 测试覆盖的代码百分比(语句、分支、路径)(Percentage of code covered by tests: statement, branch, path)
缺陷移除效率 (Defect Removal Efficiency) 发布前检测到的缺陷百分比 (% of defects detected before release)

ISO/IEC/IEEE 29119 软件测试标准

  • 适用范围: 适用于软件产品开发和维护的全过程。

  • 目标: 解决定义和程序冲突,提供组织测试策略、项目测试管理、系统与验收测试技术及非功能测试的标准。

  • 结构:

    1. 概念与词汇 (Concepts and Vocabulary): 提供软件测试概念和词汇以增强可理解性。
    2. 测试文档 (Test Documentation): 包括组织测试策略、项目测试计划、测试完成报告等。
    3. 测试流程 (Test Processes): 包括组织测试流程、测试管理流程和基本测试流程。
    4. 测试技术与覆盖率 (Testing Techniques and Coverage):
      • 静态测试 (Static Testing, e.g., reviews, inspections)
      • 动态测试 (Dynamic Testing, e.g., white box and black box)
      • 非功能测试 (Non-functional Testing)
      • 测试测量 (Test Measurement)
  • 验证与确认:

    • 验证 (Verification): 确保测试项符合规范、要求或其他文档。
    • 确认 (Validation): 确保测试项满足利益相关者的需求。

白盒测试 (White Box Testing)

  • 别名: 结构测试 (Structural Testing)
  • 特点:
    • 关注软件的内部结构。
    • 测试用例的选择基于软件实体的实现。
    • 根据覆盖标准(如路径覆盖、分支覆盖、语句覆盖)评估预期结果。
  • 验证内容:
    • 所有测试通过。
    • 所有代码语句被执行 (语句覆盖, Statement Coverage)。
    • 每个条件的每个分支被执行 (分支覆盖, Branch Coverage)。
    • 所有路径被遍历 (路径覆盖, Path Coverage)。

黑盒测试 (Black Box Testing)

  • 别名: 基于规格的测试 (Specification-based Testing)、功能测试 (Functional Testing)
  • 特点:
    • 关注输入域、预期行为、规格和软件知识。
    • 不考虑代码内部结构。
  • 测试内容:
    • 基于特定数据设计测试用例。
    • 确定函数或子函数是否按预期工作。
    • 识别软件的函数和子函数。
    • 识别与测试相关的数据属性和执行条件。
    • 测试每个功能是否按预期执行。

黑盒测试与白盒测试比较

特性 黑盒测试 (Black Box Testing) 白盒测试 (White Box Testing)
定义 基于软件规格和预期行为测试,不考虑内部结构 基于软件内部结构和代码测试
测试依据 输入域(input domain)、功能需求 代码实现、程序结构
覆盖标准 功能覆盖(function coverage) 语句覆盖(statement coverage)、分支覆盖(branch coverage)、路径覆盖(path coverage)
适用场景 验证功能是否符合要求(verify if meets expected behaviour) 验证代码逻辑和执行路径

圈复杂度 (Cyclomatic Complexity)

  • 定义: 圈复杂度是一种衡量程序复杂度的软件指标。

  • 计算公式:

    $$ \text{Cyclomatic Complexity} = E - N + 2 $$

    其中:

    • $E$ = 边数 (Number of edges)
    • $N$ = 节点数 (Number of nodes)
  • 意义:

    • 表示程序中独立路径的数量。
    • 等于程序中条件的数量。
    • 测试所有控制语句所需的测试用例数量等于圈复杂度。
    • 但圈复杂度不代表测试的充分性(即使所有路径被执行,路径的所有组合可能未被测试)。

或者也可以使用公式: $\text{Cyclomatic Complexity} = DP + 1$ DP 是决策点

示例代码

以下代码用于计算圈复杂度:

if (c1()) {
    f1();
} else {
    f2();
}
if (c2()) {
    f3();
} else {
    f4();
}
flowchart TD
    A[Start] --> B{c1 ?}
    B -- 是 --> C[f1]
    B -- 否 --> D[f2]
    C --> E{c2 ?}
    D --> E
    E -- 是 --> F[f3]
    E -- 否 --> G[f4]
    F --> H[End]
    G --> H
  • 计算:
    • 程序流图有 8 条边,7 个节点:
    • $E = 8$, $N = 7$
    • 圈复杂度 = $8 - 7 + 2 = 3$
    • 独立路径数量为 3,需设计 3 个测试用例以覆盖所有路径。

其他示例:

  1. 条件语句:
int a = 1;
int b = 2;
if (a > b) {
    cout << "a is greater";
} else {
    cout << "b is greater";
}
  • 圈复杂度 = 2(一个条件,两个路径:a > b 和 a ≤ b)。
  1. 循环语句:
int i = 1;
for (i = 1; i <= 5; i++) {
    cout << i << endl;
}
graph TD
    A[Start] --> B[int i = 1];
    B --> C{Is i <= 5?};
    C -- Yes --> D[cout << i << endl;];
    D --> E[i++];
    E --> C;
    C -- No --> F[End];
  • 圈复杂度 = 2(一个循环条件,两个路径:进入循环和退出循环)。

可能考法与示例考题

可能考法

  1. 定义与概念:
    • 解释软件测试的定义和目标。
    • 区分验证 (Verification) 和确认 (Validation)。
  2. 黑盒测试与白盒测试:
    • 比较黑盒测试和白盒测试的区别。
    • 描述各自的覆盖标准。
  3. 圈复杂度:
    • 计算给定代码的圈复杂度。
    • 列出独立路径。
  4. 测试指标:
    • 解释常见测试指标(如测试用例通过率、缺陷密度)。
  5. 费根检查:
    • 描述费根检查的步骤和应用场景。

示例考题

  1. 考题: 解释黑盒测试和白盒测试的区别,并列出各自的优缺点。

    • 参考答案 (中文):
      • 黑盒测试 (Black Box Testing):
        • 定义:基于软件规格和功能需求测试,不考虑内部代码结构。
        • 优点:专注于用户需求,测试用例设计简单,适合验收测试。
        • 缺点:无法发现代码内部逻辑错误,覆盖率可能不足。
      • 白盒测试 (White Box Testing):
        • 定义:基于代码内部结构测试,关注语句、分支和路径覆盖。
        • 优点:能发现代码逻辑错误,覆盖率高。
        • 缺点:需要了解代码结构,测试用例设计复杂。
    • Reference Answer (English):
      • Black Box Testing:
        • Definition: Testing based on software specifications and functional requirements without considering internal code structure.
        • Advantages: Focuses on user requirements, simple test case design, suitable for acceptance testing.
        • Disadvantages: Cannot detect internal logic errors, coverage may be insufficient.
      • White Box Testing:
        • Definition: Testing based on internal code structure, focusing on statement, branch, and path coverage.
        • Advantages: Can detect logic errors in code, high coverage.
        • Disadvantages: Requires understanding of code structure, complex test case design.
  2. 考题: 给定以下代码,计算其圈复杂度并列出独立路径:

if (x > 0) {
    y = x * 2;
} else {
    y = x + 1;
}
if (y > 10) {
    z = y - 10;
} else {
    z = y;
}
  • 参考答案 (中文):
    • 圈复杂度计算:
      • 程序流图:2 个条件 (x > 0 和 y > 10),每个条件有 2 个分支。
      • 圈复杂度 = 条件数 + 1 = 2 + 1 = 3。
    • 独立路径:
      1. x > 0, y > 10: 执行 y = x * 2, z = y - 10。
      2. x > 0, y ≤ 10: 执行 y = x * 2, z = y。
      3. x ≤ 0: 执行 y = x + 1, z = y。
  • Reference Answer (English):
    • Cyclomatic Complexity Calculation:
      • Program flow graph: 2 conditions (x > 0 and y > 10), each with 2 branches.
      • Cyclomatic Complexity = Number of conditions + 1 = 2 + 1 = 3.
    • Independent Paths:
      1. x > 0, y > 10: Execute y = x * 2, z = y - 10.
      2. x > 0, y ≤ 10: Execute y = x * 2, z = y.
      3. x ≤ 0: Execute y = x + 1, z = y.
0%