0%

第七章 实现

第七章“实现”主要包含编码工作测试两大部分内容。

7.1 编码 (Coding)

定义:将软件设计结果翻译成某种程序设计语言,书写程序。

7.1.1 选择程序设计语言

  • 语言分类:
    • 汇编语言 (Assembly Language):将软件设计翻译成机器操作序列,表示方式不同,既困难又容易出错。
    • 高级语言 (High-Level Language):如 Java, C#, C++, C, Python, MATLAB 等。
      • 优势:
        • 一句对应多句,效率更高(实现相同功能代码量更少)。
        • 允许用户给程序变量或子程序赋予含义鲜明的名字,便于多人协作理解。
        • 使用的符号和概念符合人的习惯思维 (如 if-else, =, +)。
        • 书写、阅读、测试、调试和维护更容易
      • 应用现状:绝大多数情况下使用高级语言,极特别领域使用汇编语言。
  • 语言选择标准 (7个):
    1. 系统用户的要求:用户可能指定特定语言以方便内部维护。
    2. 可使用的编译程序:目标环境提供的编译程序限制语言范围。
    3. 可得到的软件工具:方便的软件工具利于编写和验证。
    4. 工程的规模:有些语言更适合大型程序开发。
    5. 程序员的知识:程序员是否熟练掌握该语言。
    6. 可一致性要求:软件是否需分布到不同计算机上。
    7. 应用领域:不同领域对语言有使用限制。

7.1.2 编码风格 (Coding Style)

  • 定义:编写程序时表现出的特点、习惯和逻辑思路。

  • 良好风格的养成

    (5个方面):

    1. 程序的内部文档:
      • 恰当的标识符 (含义鲜明的名字):帮助阅读者理解程序。
      • 适当的注解 (注释):程序员之间及对未来自己的重要通信手段,有助于程序理解。
    2. 程序的视觉组织 (代码排版):
      • 对程序可读性有很大影响。
      • 应利用适当的阶梯形式来表示层次结构 (如 if-then-else 的缩进)。
    3. 数据说明:
      • 次序应标准化 (按类型或数据结构)。
      • 多个变量名在同一语句说明时,应按字母顺序排列
      • 复杂数据结构应注释说明其实现方法和特点。
    4. 语句构造:
      • 每个语句应清晰而简单,不应为提高效率使程序过分复杂。
    5. 效率:
      • 处理机时间存储容量两方面。
      • 效率是性能要求,应在需求分析阶段明确。
      • 好的设计提高效率
      • 程序的效率和程序的简单程度应一致,不应牺牲清晰性和可读性不必要地提高效率。

7.2 测试基础 (Testing Basics)

7.2.1 测试阶段的根本目标

  • 尽可能多地发现并排除软件中潜藏的错误。
  • 最终把一个高质量的软件交给用户使用。
  • 强调:软件测试不可能排除所有的错误,因为技术和测试用例限制。

7.2.2 软件测试的目标 (定义)

  1. 测试是为了发现程序中错误而执行的过程。
  2. 好的测试方案是尽可能发现迄今为止尚未发现错误的测试方案。
  3. 成功的测试是发现了迄今为止尚未发现错误的测试。

7.2.3 测试准则 (7个)

  1. 所有测试都应追溯到用户需求:不符合需求是最严重的错误。
  2. 远在测试开始之前就应制定测试计划 (在设计阶段制定)。
  3. 应将二八原则 (Pareto principle) 应用到软件测试中。
  4. 从小规模测试开始,逐步进行大规模测试 (如从单元测试到验收测试)。
  5. 穷举测试是不可能的:软件路径众多,测试用例无法穷尽,因此需要逻辑覆盖等方法。
  6. 为达到最佳测试效果,应有独立的第三方进行测试工作 (测试人员与代码编写人员分开)。

7.2.4 测试方法

  • 黑盒测试 (Black-box Testing):
    • 定义:把程序看作一个黑盒子完全不考虑程序内部结构和处理过程,只在程序的接口进行测试。
    • 检查内容:程序功能是否按规格说明书正常使用,能否接收数据并产生正确输出,运行中是否保持外部信息完整性。
    • 别称功能测试
    • 理解:只关心结果是否正确,不关心过程 (类比数学题,结果对过程错,黑盒老师判对)。
    • 使用阶段:通常在后期进行。
    • 发现错误类型:功能不正确或遗漏、界面错误、数据结构错误、性能错误、初始化和终止错误。
    • 技术等价划分边界值分析错误推测
  • 白盒测试 (White-box Testing):
    • 定义:把程序装在透明盒子里,测试者完全知道程序的结构和处理算法,按照程序内部逻辑进行测试,检查执行通路是否按预定要求工作。
    • 别称结构测试
    • 理解:关心结果是否正确过程是否正确 (类比数学题,过程错结果对,白盒老师判错)。
    • 使用阶段:通常在早期使用。
    • 关注重点模块的接口、局部数据结构、重要的执行通路、出错处理通路、边界条件
    • 技术:
      • 逻辑覆盖 (Logic Coverage):选取具有代表性的软件路径进行测试,是穷尽测试唯一可行的替代办法。
        • 语句覆盖 (Statement Coverage):最弱,要求程序的每个语句都执行一次
        • 判定覆盖 (Decision Coverage / Branch Coverage):每个语句执行一次,且每一个判定结果的每种可能结果都执行一次 (每个分支至少执行一次)。
        • 条件覆盖 (Condition Coverage):每个语句执行一次,且判定表达式的每个条件都能取得各种可能结果
        • 判定/条件组合覆盖等 (要求更严格)。
    • 控制结构测试 (Control Structure Testing):
      • 基本路径测试 (Basis Path Testing):非常重要。
        • 步骤:
          1. 根据设计结果画出相应的流图
          2. 计算环形复杂度 (Cyclomatic Complexity):
            • 方法一:V(G) = P + 1 (P为判定节点数)。
            • 方法二:数闭区间数量加一。
          3. 确定线性独立路径的基本集合:独立路径指至少引入程序一条新处理语句或一条新条件路径,或一条之前未使用的边。路径数量等于环形复杂度。
          4. 设计测试用例强制执行基本集合中的每条路径。
      • 条件测试、循环测试。
  • 灰盒测试 (Grey-box Testing):介于黑盒与白盒之间的一种测试方法。

7.2.5 测试步骤 (按顺序,及发现错误类型)

  1. 模块测试 (Module Testing) / 单元测试 (Unit Testing):
    • 目标:保证每个模块作为单元正常运行。
    • 发现错误类型编码和详细设计的错误
    • 主要使用白盒测试技术
    • 手段:
      • 代码审查 (Code Review):人工进行,可由编写者非正式进行,或由审查小组正式进行 (组长、设计者、编写者、测试者组成)。高效,能查出30%-70%的逻辑设计错误和编码错误。与计算机测试互补。
      • 计算机测试 (Computer Testing):
        • 驱动软件 (Driver Software):假的主程序,接收测试数据,传输给被测试模块,并打印结果。用于测试底层模块
        • 存根软件 (Stub Software):被代替模块所调用的模块 (虚拟子程序),用于代替底层小弟接收指令。用于测试顶层模块
        • 缺陷:驱动和存根软件代表开销,增加成本。
  2. 子系统测试 (Subsystem Testing):
    • 目标:将单元模块放在一起,测试模块相互之间的协调、通信
    • 发现错误类型模块接口错误
    • 特点:兼有测试和组装两重含义,常称之为集成测试的一部分。
  3. 系统测试 (System Testing):
    • 目标:将测试的子系统装配成一个完整的整体进行测试。
    • 发现错误类型软件设计中的错误以及需求说明中的错误
    • 特点:兼有测试和组装两重含义,常称之为集成测试的一部分。
  4. 集成测试 (Integration Testing):
    • 包含:子系统测试和系统测试。
    • 模块组装方法:
      • 非渐增式 (Non-incremental):先分别测试每个模块,再全部组装成程序。
      • 渐增式 (Incremental):将下一个要测试的模块与已测试好的模块结合,每次增加一个。更彻底,易于定位错误位置,通常推荐使用。
    • 渐增式策略 (两种):
      • 自顶向下集成 (Top-down Integration):
        • 从主控模块 (上层) 开始向下移动,逐渐结合模块。
        • 优点:能在测试早期验证软件主要功能早期发现上层模块的接口错误
        • 缺点需要存根程序,底层发现错误较晚。
      • 自底向上集成 (Bottom-up Integration):
        • 从原子模块 (底层) 开始向上组装测试。
        • 优点不需要存根程序底层模块发现错误较早,能充分展示人力。
        • 缺点需要驱动程序,验证主要功能和发现上层接口错误较晚。
    • 回归测试 (Regression Testing):
      • 目的:执行已做过测试的某个子集,保证软件由于调试或其他原因引起的变动不会带来非预期副作用 (如引入新的错误)。
  5. 验收测试 (Acceptance Testing) / 确认测试 (Confirmation Testing):
    • 目标:把软件作为一个单一实体进行测试,验证软件的有效性 (软件功能和性能如同用户合理期待)。
    • 特点用户积极参与,主要使用实际数据
    • 发现错误类型需求规格说明书中的错误
    • 主要使用黑盒测试
    • 重要内容软件配置复查,确保所有成分齐全,质量符合要求,文档和程序一致,便于维护。
    • 分类:
      • 阿尔法测试 (Alpha Testing):
        • 开发者场所进行,开发者在用户指导下进行,记录错误,受控环境。开发者是主导地位。
      • 贝塔测试 (Beta Testing):
        • 最终用户在一个或多个客户场所进行,开发者通常不在场,软件在开发者不能控制的环境下进行真实应用。用户拥有更多权利。
  6. 平行运行 (Parallel Running):
    • 定义同时运行新开发的系统和即将被取代的旧系统
    • 目的/优点:
      • 可在准生产环境中运行新系统,不冒风险。
      • 用户有时间熟悉新系统
      • 可以验证用户手册等文档。
      • 以准生产模式验证性能指标 (如负载冲击测试)。

7.2.6 测试阶段的信息流 (使用的文档资料)

  • 软件配置文档:
    • 需求说明书:用于编写用例,检查是否满足需求。
    • 设计说明书:用于检测软件设计问题和进行白盒测试。
    • 源程序清单 (代码)。
  • 测试相关文档:
    • 测试计划
    • 测试方案:不仅包含输入数据,还需有每组数据预期检验的功能和预期得到的正确输出 (像有题有答案)。

7.8 调试 (Debugging)

  • 定义:作为成功测试的后果出现,是发现错误后排错的过程
  • 过程:执行测试用例 -> 产生结果 -> 调试 (一眼识出/推测验证) -> 确认改正。
  • 调试困难的原因 (心理、技术、软件固有特征):
    • 症状与问题发生地距离远。
    • 改正错误后症状暂时消失。
    • 症状并非由错误引起。
    • 症状可能由不易跟踪的人为引起。
    • 症状可能由定时问题引起。
    • 很难产生完全一样的输入条件 (难以复现)。
    • 症状时有时无。
    • 症状可能分布在多个任务中。
  • 调试的途径 (3种):
    1. 蛮干法 (Brute-force Method):其他方法失效后的最后手段,地毯式搜索。
    2. 回溯法 (Backtracking Method)常用调试方法,从发现症状的地方人工沿控制流程回溯追踪错误点,小程序适用
    3. 原因排除法 (Cause Elimination Method):
      • 对分查找法 (Binary Partitioning):将程序一分为二,逐步缩小错误范围。
      • 归纳法 (Induction):从个别现象推断一般结论,组织分析错误数据。
      • 演绎法 (Deduction):从原理和前提出发,设想问题原因并逐个验证。

7.9 软件可靠性 (Software Reliability)

  • 定义:
    • 软件可靠性:在给定时间间隔内成功运行的概率。
    • 软件可用性 (Availability):在给定时间点成功运行的概率。
  • 估算平均无故障时间的方法MTTF (Mean Time To Failure) 是一个重要的参考指标。