软件测试基础-黑盒测试、白盒测试、测试用例设计

软件测试的心理学和经济学

软件测试的心理学

  • 测试是为发现错误而执行程序的过程
  • 测试的“成功”与“不成功”
    • 成功:在测试某段程序时发生了错误,这些错误可以修复/本次测试可以最终确定再无其他可查出的错误
    • 不成功:未能适当地对程序进行检查,大多情况下未能找出错误的测试
  • 软件测试:为试图发现程序中错误(假设其存在)的破坏性的过程,一个成功的测试用例,通过诱发程序发生错误,可以在这个方向上促进软件质量的改进。

软件测试的经济学

黑盒测试

  • 测试目标与程序内部机制和结构完全无关,将重点集中放在发现程序不按其规范正确运行的环境条件
  • 判定标准:“穷举输入测试”
  • 穷举不可能,因此需要设计所有有效等价类测试用例和无效等价类测试用例

白盒测试(逻辑驱动测试)

  • 允许检查程序内部结构,这种测试策略对程序的逻辑结构进行检查,从中获取测试数据
  • 穷举路径测试:使用测试用例执行所有可能的控制流路径
  • 缺点:路径数量多,不能保证程序符合其涉及规范,可能会缺少某些路径存在问题,不会暴露数据敏感错误

软件测试的原则

  • 测试用例中一个必需部分是对预期输出或结果进行定义
  • 程序员应当避免自己编写的程序
  • 编写软件的组织不应当测试自己编写的软件
  • 应当彻底检查每个测试的执行结果
  • 测试用例的编写不仅应当根据有效或预料到的输入情况,而且也应当根据无效和未预料到的输入情况
  • 检查程序是否“未做其应该做的”仅是测试的一半,测试的另一半是检查程序是否“做了其不应该做的”、
  • 应避免测试用例用后即弃,除非软件本身就是一个一次性的软件
    • 回归测试:保留测试用例,当程序其他部件发生更动后重新执行
  • 计划测试工作时不应默许假定不会发现错误
  • 程序某部分存在更多错误的可能性,与该部分已发现错误的数量整正比

代码检查、走查与评审

代码检查、走查

  • 人工测试方法
  • “头脑风暴会”:找出错误,但不必找出改正错误的方法
  • 优点:一旦发现错误,能够在代码中进行精确定位,降低调试成本

代码检查

  • 由程序编码人员逐条语句讲述程序的逻辑结构。讲述过程中,其他成员应该提问题、判断是否存在错误。
  • 对历来常见的编码错误列表分析程序

用于代码检查的错误列表

数据引用错误

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

数据声明错误在这里插入图片描述运算错误

在这里插入图片描述
在这里插入图片描述

比较错误

在这里插入图片描述
在这里插入图片描述

控制流程错误

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

接口错误

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

输入/输出错误

在这里插入图片描述

其他检查

在这里插入图片描述在这里插入图片描述

代码走查

  • 测试用例开会推演

桌面检查

  • 单人进行的代码检查或走查

同行评分

在这里插入图片描述

测试用例的设计

  • 先使用黑盒测试方法来设计测试用例,视情况使用白盒测试方法来设计补充的测试用例。
    在这里插入图片描述

白盒测试

在这里插入图片描述

逻辑覆盖测试

  • 语句覆盖:设计的测试用例,可以覆盖每一条语句。
    • 一定能覆盖判定覆盖的测试用例
    • 存在某些条件的任意结果取值都没有影响
    • 可能会缺少某些路径
  • 判定覆盖:必须编写足够的测试用例,使得每一个判断都至少有一个为真和为假的输出结果。
    • 判定覆盖可以满足语句覆盖
    • 三个例外情况:
      • 程序中不存在判断
      • 程序或子程序/方法由着多重入口点。只有从程序的特定入口点进入时,某条特定的语句才可能执行到
      • 在ON单元的语句中,遍历每条分支路径并不一定能确保所有的ON单元都能执行到
    • 对于多重选择:判定覆盖准则将所以有判断的每个可能结果都至少执行一次,以及将程序或子程序的每个入口点都至少执行一次
  • 条件覆盖:编写足够的测试用例以确保将一个判断中的每个条件的所有可能的结果至少执行一次。
    • 有时可能不能满足判定覆盖的要求,只涵盖了部分判定结果
  • 判定/条件覆盖:将一个判断中的每个条件的所有可能的结果至少执行一次,将每个判断的所有可能的结果至少执行一次,将每个入口点都至少调用一次
    • 条件覆盖或判定/条件覆盖准则不一定会发现逻辑表达式中的错误
  • 多重条件覆盖:将每个判定中所有可能的条件结果的组合

黑盒测试

等价类划分

  • 步骤
    • 确定等价类:选取每一个输入条件将其划分为两个或更多的组
      • 有效等价类:程序的有效输入
      • 无效等价类:其他任何可能的输入条件
    • 生成测试用例
      • 为每个等价类设置一个不同的编号
      • 编写新的测试用例,尽可能多地覆盖那些尚未被涵盖的有效等价类,知道所有的有效等价类都被测试用例所覆盖
      • 编写新的用例,覆盖一个且仅一个尚未被涵盖的无效等价类,直到所有的无效等价类都被测试用例锁覆盖

边界值分析

  • 边界条件的测试用例会具有更高的测试回报率
  • 边界条件:指的是输入和输出等价类中那些恰好于边界、或超过边界、或在边界以下的状态

因果图

  • 边界值分析和等价划分的一个弱点是未对输入条件的组合进行分析。
  • 步骤
    • 将规格说明分解为可执行的片段
    • 确定规格说明中的因果关系。
      • 因:一个明确的输入条件或输入条件的等价类
      • 果:一个输出条件或系统转换
    • 分析规格说明的语义内容,并将其转换为连接因果关系的布尔图
    • 给图加上注解符号,说明由于语法或环境的限制而不能联系起来的“因”和“果”
    • 通过仔细跟踪图中的状态变化情况,将因果图转换成一个有限项的判定表。表中的每一列代表一个测试用例
    • 将判定表中的列转换成测试用例。

错误猜测

  • 基本思想:列举出可能犯的错误或错误易发情况的清单,然后依据名单来编写测试用例。
  • 另一种思想:在阅读规格说明时联系程序员可能做得假设来确定测试用例

测试策略

  • 如果规格说明中包含输入条件组合的情况,应首先使用因果图分析方法
  • 在任何情况下都应使用边界值分析方法
  • 应为输入和输出确定有效和无效等价类,必要情况下对上面确认的测试用例进行补充
  • 使用错误猜测技术增加更多的测试用例
  • 针对上述测试用例集检查程序的逻辑结构。
    • 判定覆盖
    • 条件覆盖
    • 判断/条件覆盖
    • 多重条件覆盖

模块测试

  • 模块测试(单元测试):对程序中的单个子程序、子程序或过程进行测试的过程。
  • 目的:将模块的功能和定义模块的功能规格说明或接口规格说明进行比较

测试用例设计

  • 模块测试总体面向白盒测试
  • 模块测试的用例设计过程:使用一种或多种白盒测试方法分析模块的逻辑结果,然后使用黑盒测试方法对照模块的规格说明以补充测试用例

增量测试

  • 非增量测试:先独立地测试每个模块,然后再将这些模块组装成完整的程序
  • 增量测试:先将下一步要测试的模块组装到测试完成的模块集合中,然后再进行测试
  • 结论
    • 非增量测试所需的工作量要多
    • 如果使用增量测试,可以较早地发现模块中与不匹配接口、不正确假设相关的编程错误,尽早对模块组合进行集成测试
    • 使用增量测试,调试会更加容易
    • 增量测试会将测试进行的更加彻底,增量测试可能会暴露之前测试过的模块未发现的缺陷;非增量测试只对测试模块本身有影响。增量测试用使用先前测试过的模块,取代了非增量测试中使用的桩模块或驱动模块
    • 非增量测试所占用的及其时间少,需要的驱动模块和桩模块更多
    • 非增量测试可以进行并行操作

自顶向下测试和自底向上测试

  • 增量测试要优于非增量测试。
  • 自顶向下测试和自底向上测试都是属于增量测试

自顶向下测试

  • 从程序的顶部或初始模块开始。要成为合乎条件的下一个模块,至少一个该模块的从属模块事先经过了测试
  • 不足:无法建立所有测试环境

自底向上测试

  • 开始于程序中的终端模块(此类模块不再调用其他任何模块)。要成为合乎条件的下一个模块,该模块的所有从属模块(它调用的模块)事先经过了测试
  • 不足:没有早期程序框架的概念

比较

自顶向下测试

  • 优点
  1. 如果主要的缺陷发上在程序的顶层将非常有利
  2. 一旦引入I/O功能,提交测试用例会更加容易
  3. 早期的程序框架可以进行演示,并可激发积极性
  • 缺点
  1. 必须开发桩模块
  2. 桩模块要比最初表现的更复杂
  3. 在引入I/O功能之前,向桩模块引入测试用例比较困难
  4. 创建测试环境可能困难,甚至无法实现
  5. 观察测试输出很困难
  6. 使人误解设计和测试可以交迭进行
  7. 会导致特定模块的完成延后

自底向上测试

  • 优点
  1. 如果主要的缺陷发生在程序的底层将非常有利
  2. 测试环境比较容易建立
  3. 观察测试输出比较容易
  • 缺点
  1. 必须开发驱动模块
  2. 直到最后一个模块添加进去,程序才形成一个整体

执行测试

  • 测试实际结果与预期结果不匹配
  • 模块存在错误
  • 测试用例不正确(需要对测试用例进行审查或检查)
  • 使用自动化测试减少重复劳动,降低对驱动模块代码的需求
  • 执行测试时,应该找出程序的副作用(模块执行了某些不该执行操作的情况)
  • 测试用例应该记录下来
  • 如果某个模块存在大量错误,那么这个模块可能包含更多的错误,需要进一步测试
  • 模块测试的目的是证明模块中存在错误