软件实现与测试

软件测试方法

软件工程 第四部分 第10章,主讲人:李欣

Created: 2024-03-05 Tue 16:42

0.1. 互动课堂

Click to host the seminar.

0.2. 签到

https://dash.memopixel.com/tool/attendance/

0.3. 本次课的目标

  • 第四部分:软件实现与测试
    • 第10章:软件测试方法
      • 软件测试的基本概念
      • 白盒测试的测试用例设计
      • 基本路径覆盖
      • 黑盒测试的测试用例设计
      • 软件测试的策略
      • 人工测试
      • 自动化测试
      • 调试

1. 软件测试的基本概念

1.1. 什么是软件测试

软件测试 是为了 发现错误执行程序 的过程。

或者说, 软件测试

  • 是根据软件开发各阶段的 规格说明程序的内部结构 而精心设计一批 测试用例 (即 输入数据 及其 预期的输出结果 ),
  • 并利用这些 测试用例运行程序 ,以 发现程序错误 的过程。

1.2. 软件测试的目的和原则

Glenford J. Myers \(^*\) 提出的软件测试的目的
  • 测试 是程序的 执行过程目的 在于发现 错误
    • 一个好的 测试用例 在于能发现 至今未发现错误
  • 一个 成功的测试 是发现了 至今未发现错误测试
相应的软件测试的原则
  • 应当把 尽早地不断地 进行 软件测试 作为软件开发者的座右铭。
  • 测试用例应由 测试输入数据 和相应的 预期输出结果 两部分组成。
  • 程序员应避免检查 自己的 程序。
  • 设计测试用例时,应包括 合理输入条件不合理输入条件
  • 充分注意测试中的 群集现象
  • 严格执行 测试计划 ,排除测试的随意性。
  • 应当对每一个测试结果做 全面检查
  • 妥善保存 测试计划测试用例出错统计 和最终 分析报告 ,为未来实施的维护提供方便。

\(^*\) https://en.wikipedia.org/wiki/Glenford_Myers

1.3. 软件测试的对象

软件测试 并不等于程序测试。软件测试应贯穿于软件 定义开发整个期间

  • 因此 需求分析概要设计详细设计 以及 程序编码 等各阶段所得到的文档资料,
  • 包括 需求规格说明概要设计规格说明详细设计规格说明 以及 源程序
  • 都应成为 软件测试对象

据美国一家公司的统计表明,在查找出的软件错误中,属于 需求分析软件设计 的错误约占 \(64\%\) , 属于程序编写的错误仅占 \(36\%\) 。

  • 到程序的测试为止,软件开发工作已经历了许多环节,每个环节都可能发生问题。 为把握各环节正确性,需进行各种 确认验证 工作。

1.4. 测试信息流

测试过程需要的三类输入
  • 软件配置 :包括 软件需求规格说明软件设计规格说明源代码 等。
  • 测试配置 :包括 测试计划测试用例测试驱动程序 等。 从整个软件工程过程看, 测试配置软件配置 的一个子集。
  • 测试工具 :为提高软件测试效率,测试工作需要 测试工具 的支持, 它们的工作是为测试的实施提供某种 服务 ,以减轻人们完成测试任务中的手工劳动。
    • 测试数据自动生成程序
    • 静态分析程序
    • 动态分析程序
    • 测试结果分析程序
    • 驱动测试的测试数据库
    • ……

test-information-flow.png

Figure 1: 测试信息流

1.5. 测试与软件开发各阶段的关系

  • 开发过程 自顶向下、逐步细化
  • 测试过程 自底向上、逐步集成

test-development-relationships.png

Figure 2: 软件测试与软件开发过程的关系

1.6. 白盒测试与黑盒测试

  • 黑盒测试 :已知产品的 功能设计规格 ,可以通过测试证明每个 实现了的功能是否符合要求
  • 白盒测试 :已知产品的 内部工作过程 ,可以通过测试证明每种 内部操作是否符合设计规格要求 , 所有内部成分是否已经过检查。

1.6.1. 黑盒测试

黑盒测试方法主要是为了发现
  • 是否有不正确或遗漏了的 功能
  • 输入 能否正确地接受?
  • 能否输出正确的 结果
  • 是否有 数据结构错误 或外部信息(例如数据文件) 访问错误
  • 性能 上是否能够满足要求?
  • 是否有 初始化终止性 错误?

用黑盒测试发现程序中的错误,必须在 所有 可能的 输入条件输出条件 中确定 测试数据 , 检查程序是否都能产生正确的输出。

假设一个 程序 \(P\) 有 输入量 \(X\) 和 \(Y\) 及 输出量 \(Z\) ,在字长为 32位 的计算机上运行。 如果 \(X, Y\) 只取整数,考虑把所有的 \(X, Y\) 值都作为测试数据,按 黑盒 方法进行 穷举测试

  • 这样做可能采用的测试数据组为 \((X_i, Y_i)\) ,
  • 不同测试数据组合的最大可能数目为 \(2^{32} \times 2^{32} = 2^{64}\) 。
%%{init: { 'theme': 'forest', 'fontFamily': 'DejaVu Sans' }}%%
flowchart LR
    style X stroke: none
    style X fill: none
    style Y stroke: none
    style Y fill: none
    style Z stroke: none
    style Z fill: none
    X["X"] --> P["<br>P<br><br><br>"]
    Y["Y"] --> P
    P --> Z["Z"]

blackbox.svg

Figure 3: 黑盒子

  • 如果程序 \(P\) 测试一组 \(X, Y\) 数据需要1毫秒,而且假定一天工作24小时, 一年工作365天,要完成 \(2^{64}\) 组测试,需要5亿年。

1.6.2. 白盒测试

软件的白盒测试
  • 是对软件的 过程性细节 做细致的检查,
  • 允许测试人员利用程序内部的 逻辑结构 及有关信息, 设计或选择
    测试用例 ,对程序所有 逻辑路径 进行测试。
  • 又称为 结构测试逻辑驱动测试
软件人员使用白盒测试方法时需做到的几点
  • 对程序模块的所有独立的 执行路径 至少测试一次;
  • 对所有的 逻辑判定 ,取 与取 的两种情况都 至少测试一次
  • 在循环的 边界 和运行 界限执行循环体
  • 测试 内部数据结构 的有效性。
  • ……

对于一个具有 多重选择循环嵌套 的程序, 独立路径 数目可能是天文数字。
穷举测试 由于 工作量过大需要的时间过长 ,实施起来是 不现实 的。

2. 白盒测试的测试用例设计

2.1. 逻辑覆盖

逻辑覆盖 是以程序内部的 逻辑结构 为基础的设计测试用例的技术,它属于 白盒测试

根据覆盖测试目标不同可将逻辑覆盖分为
  • 语句覆盖
  • 判定覆盖
  • 条件覆盖
  • 判定-条件覆盖
  • 条件组合覆盖
  • 路径覆盖

test-case-design-example.png

Figure 4: 测试用例设计的参考例子

\(L_1(a \rightarrow c \rightarrow e)\)
\(=\{(A > 1) and (B = 0)\} and \{(A = 2) or (\frac{X}{A} > 1)\}\) \(=(A > 1) and (B = 0) and (A = 2) or (A > 1) and (B = 0) and (\frac{X}{A} > 1)\) \(=(A = 2) and (B = 0) or (A > 1) and (B = 0) and (\frac{X}{A} > 1)\)

\(L_2(a \rightarrow b \rightarrow d)\)
\(=\{\overline{(A > 1) and (B = 0)}\} and \{\overline{(A = 2) or (X > 1)}\}\) \(=\{(\overline{A > 1}) or (\overline{B = 0})\} and \{(\overline{A = 2}) and (\overline{X > 1})\}\) \(=(\overline{A > 1}) and (\overline{A = 2}) and (\overline{X > 1}) or (\overline{B = 0}) and (\overline{A = 2}) and (\overline{X > 1})\) \(=(A \le 1) and (X \le 1) or (B \neq 0) and (A \neq 2) and (X \le 1)\)

\(L_3(a \rightarrow b \rightarrow e)\)
\(=\{\overline{(A > 1) and (B = 0)}\} and \{(A = 2) or (X > 1)\}\) \(=\{(\overline{A > 1}) or (\overline{B = 0})\} and \{(A = 2) or (X > 1)\}\) \(=\{(A \le 1) and [(A = 2) or (X > 1)] or (B \neq 0) and [(A = 2) or (X > 1)]\}\) \(=(A \le 1) and (X > 1) or (B \neq 0) and (A = 2) or (B \neq 0) and (X > 1)\)

\(L_4(a \rightarrow c \rightarrow d)\)
\(=\{(A > 1) and (B = 0)\} and \{\overline{(A = 2) or (\frac{X}{A} > 1)}\}\) \(=(A > 1) and (B = 0) and (A \neq 2) and (\frac{X}{A} \le 1)\)

2.2. 语句覆盖

语句覆盖 是设计若干个测试用例,运行被测程序,使得 每一个可执行语句至少执行一次

测试用例的设计格式如下:

【输入的 \((A, B, X)\) ,输出的 \((A, B, X)\) 】

满足语句覆盖要求的测试用例是
参与人数 0
A 0
B 0
C 0
D 0
,A ,B ,C ,D ,0 ,0 ,0 ,0

2.3. 判定覆盖

判定覆盖 是设计若干个测试用例,运行被测程序,使得 程序中每个判断的取真分支和取假分支至少经历一次

判定覆盖又称为 分支覆盖

测试用例举例1
  • \(L_1(a \rightarrow c \rightarrow e)\) : \([(2, 0, 4), (2, 0, 3)]\)
  • \(L_2(a \rightarrow b \rightarrow d)\) : \([(1, 1, 1), (1, 1, 1)]\)
测试用例举例2
  • \(L_3(a \rightarrow b \rightarrow e)\) : \([(2, 1, 1), (2, 1, 2)]\)
  • \(L_4(a \rightarrow c \rightarrow d)\) : \([(3, 0, 3), (3, 0, 1)]\)

2.4. 条件覆盖

条件覆盖 是设计若干个测试用例,运行被测程序,使得 程序中每个判断的每个条件的可能取值至少执行一次

对于第一个判断
  • 条件 \(A>1\) 取真值为 \(T_1\) ,取假值为 \(\overline{T_1}\) ;
  • 条件 \(B=0\) 取真值为 \(T_2\) ,取假值为 \(\overline{T_2}\) ;
对于第二个判断
  • 条件 \(A=2\) 取真值为 \(T_3\) ,取假值为 \(\overline{T_3}\) ;
  • 条件 \(X>1\) 取真值为 \(T_4\) ,取假值为 \(\overline{T_4}\) ;
测试用例举例1
测试用例 通过路径 条件取值 覆盖分支
\([(2, 0, 4), (2, 0, 3)]\) \(ace (L_1)\) \(T_1 T_2 T_3 T_4\) \(c, e\)
\([(1, 0, 1), (1, 0, 1)]\) \(abd (L_2)\) \(\overline{T_1} T_2 \overline{T_3} \overline{T_4}\) \(b, d\)
\([(2, 1, 1), (2, 1, 2)]\) \(abe (L_3)\) \(T_1 \overline{T_2} T_3 \overline{T_4}\) \(b, e\)
测试用例举例2
测试用例 通过路径 条件取值 覆盖分支
\([(1, 0, 3), (1, 0, 4)]\) \(abe (L_3)\) \(\overline{T_1} T_2 \overline{T_3} T_4\) \(b, e\)
\([(2, 1, 1), (2, 1, 2)]\) \(abe (L_3)\) \(T_1 \overline{T_2} T_3 \overline{T_4}\) \(b, e\)

2.5. 判定-条件覆盖

判定-条件覆盖 是设计足够的测试用例,使得 判断中每个条件的所有可能取值至少执行一次 , 同时 每个判断本身的所有可能判断结果至少执行一次

测试用例 通过路径 条件取值 覆盖分支
\([(2, 0, 4), (2, 0, 3)]\) \(ace (L_1)\) \(T_1 T_2 T_3 T_4\) \(c, e\)
\([(1, 1, 1), (1, 1, 1)]\) \(abd (L_2)\) \(\overline{T_1} \overline{T_2} \overline{T_3} \overline{T_4}\) \(b, d\)

2.6. 条件组合覆盖

条件组合覆盖 是设计足够的测试用例,运行被测程序,使得 每个判断的所有可能的条件取值组合至少执行一次

  1. \(A > 1, B = 0\) 记作 \(T_1 T_2\) ,属第一个判断的取 分支;
  2. \(A > 1, B \neq 0\) 记作 \(T_1 \overline{T_2}\) ,属第一个判断的取 分支;
  3. \(A \le 1, B = 0\) 记作 \(\overline{T_1} T_2\) ,属第一个判断的取 分支;
  4. \(A \le 1, B \neq 0\) 记作 \(\overline{T_1} \overline{T_2}\) ,属第一个判断的取 分支;
  5. \(A = 2, X > 1\) 记作 \(T_3 T_4\) ,属第二个判断的取 分支;
  6. \(A = 2, X \le 1\) 记作 \(T_3 \overline{T_4}\) ,属第二个判断的取 分支;
  7. \(A \neq 2, X > 1\) 记作 \(\overline{T_3} T_4\) ,属第二个判断的取 分支;
  8. \(A \neq 2, X \le 1\) 记作 \(\overline{T_3} \overline{T_4}\) ,属第二个判断的取 分支。
测试用例 通过路径 覆盖条件 覆盖组合号
\([(2, 0, 4), (2, 0, 3)]\) \(ace (L_1)\) \(T_1 T_2 T_3 T_4\) \(1, 5\)
\([(2, 1, 1), (2, 1, 2)]\) \(abe (L_3)\) \(T_1 \overline{T_2} T_3 \overline{T_4}\) \(2, 6\)
\([(1, 0, 3), (1, 0, 4)]\) \(abe (L_3)\) \(\overline{T_1} T_2 \overline{T_3} T_4\) \(3, 7\)
\([(1, 1, 1), (1, 1, 1)]\) \(abd (L_2)\) \(\overline{T_1} \overline{T_2} \overline{T_3} \overline{T_4}\) \(4, 8\)

2.7. 路径覆盖

路径覆盖 是设计足够的测试用例,覆盖 程序中所有可能的路径

测试用例 通过路径 覆盖条件
\([(2, 0, 4), (2, 0, 3)]\) \(ace (L_1)\) \(T_1 T_2 T_3 T_4\)
\([(1, 1, 1), (1, 1, 1)]\) \(abd (L_2)\) \(\overline{T_1} \overline{T_2} \overline{T_3} \overline{T_4}\)
\([(1, 1, 2), (1, 1, 3)]\) \(abe (L_3)\) \(\overline{T_1} \overline{T_2} \overline{T_3} T_4\)
\([(3, 0, 3), (3, 0, 1)]\) \(acd (L_4)\) \(T_1 T_2 \overline{T_3} \overline{T_4}\)

3. 基本路径覆盖

3.1. 程序的控制流图

控制流图 是描述程序的 控制流 的一种图示方法。

control-flow-graph-blocks.png

Figure 5: 控制流图的各种图形符号

  • 结点 在控制流图中用符号 〇 表示,它代表一个或多个 无分支PDL语句源程序语句
  • 必须 终止于 结点 ,但在 选择多分支 结构中分支的汇聚处, 即使没有执行语句也应该有一个 汇聚结点
  • 区域结点 圈定的范围,对区域计数时, 图形外的范围 也应记为一个区域。

program-flowchart-and-control-flow-graph.png

Figure 6: 程序流程图与对应的控制流图

当判断中的条件表达式是 复合条件 时,需要改复合条件为一系列只有 单个条件嵌套 的判断。

control-flow-graph-with-composite-logic.png

Figure 7: 复合逻辑下的控制流图

3.2. 程序环路复杂性

对于给定的 控制流图 \(G\)

McCabe 给出的 环路复杂性 \(V(G)\) 的计算方法
  • 环路复杂性定义为控制流程图中的 区域数
  • 设 \(E\) 为控制流图的 边数 , \(N\) 为图中的 结点数 , 则 \(V(G) = E - N + 2\) 。
  • 设 \(P\) 为控制流图中的 判定结点数 ,则 \(V(G) = P + 1\) 。

从程序的 环路复杂性 可导出程序基本路径集合中的 独立路径数

独立路径 是指包括一组 以前没有处理语句条件 的一条路径。

control-flow-graph.png

Figure 8: 控制流图

该控制流图的一个基本路径集
  • \(path1\)
    • \(1-11\)
  • \(path2\)
    • \(1-2-3-4-\) \(5-10-1-11\)
  • \(path3\)
    • \(1-2-3-6-\) \(8-9-10-1-11\)
  • \(path4\)
    • \(1-2-3-6-\) \(7-9-10-1-11\)

3.3. 导出测试用例

  • 详细设计源代码 作为基础,导出程序的 控制流图
  • 计算得到的 控制流图 \(G\) 的 环路复杂性 \(V(G)\) ;
  • 确定线性无关的 基本路径集
  • 生成 测试用例 ,确保基本路径集中 每条路径的执行

4. 黑盒测试的测试用例设计

4.1. 等价类划分

等价类划分 是一种典型的 黑盒测试 方法,也是一种非常实用的重要测试方法。

步骤
  • 划分等价类(列出等价类表)
  • 选取测试用例

4.1.1. 划分等价类

等价类 是指每个 输入域子集合 , 在该 子集合 中, 各个输入数据对于揭露程序中的错误都是等效的

  • 把数目极多的 输入数据(有效的和无效的) 划分为若干个 等价类 。 并合理地假定: 测试某等价类的代表值 等价于 对这一类其他值的测试 。即:
    • 如果用某个 等价类 中的 一个数据 作为测试数据进行测试 查出了错误 , 则使用 该等价类 中的 其他数据 进行测试也会 查出同样的错误
    • 如果用某个 等价类 中的 一个数据 作为测试数据进行测试 没有查出错误 , 则使用 该等价类 中的 其他数据 进行测试也 同样查不出错误

因此,可以把 全部可供输入的数据 合理划分为 若干等价类 , 在每一个等价类中取一个数据作为测试的输入, 这样就可以 用少量代表性测试数据,达到测试的要求

等价类划分的两种不同情况
  1. 有效等价类 :是指对于软件的 规格说明 来说, 合理的有意义输入数据构成的集合 。 用于检验程序是否实现了规格说明预先规定的 功能性能
  2. 无效等价类 :是指对于软件的 规格说明 来说, 不合理的无意义输入数据构成的集合 。 用于检查程序中 功能性能 的实现是否有不符合规格说明要求的情况。

在设计测试用例时,要同时考虑 有效等价类无效等价类 。 软件不能都只接受合理的数据,还要经受意外的考验, 检验出无效的或不合理的数据,这样的软件测试才是全面性的

划分等价类的原则
  1. 如果输入数据规定了 取值范围值的个数 , 则可以确定 一个 有效等价类两个 无效等价类
  2. 如果规格说明规定了 数据值的集合 ,或者是规定了 必须如何 的条件, 则可以确定 一个 有效等价类一个 无效等价类
  3. 如果规格说明规定了一个 条件数据 ,则可确定 一个 有效等价类一个 无效等价类
  4. 如果已划分的等价类中 各元素在程序中的处理方式不同 , 则应将此等价类进一步划分成 更小的等价类

4.1.2. 确定测试用例

在确定了等价类之后,建立 等价类表 ,列出所有划分出的等价类:

输入数据 有效等价类 无效等价类
…… …… ……
…… …… ……
从划分出的等价类中选择测试用例时应遵循的原则
  1. 为每一个等价类规定一个 唯一编号
  2. 设计一个新的测试用例,使其 尽可能多地覆盖 尚未被覆盖的有效等价类 , 重复这一步,直到 所有的有效等价类 都被 覆盖 为止;
  3. 设计一个新的测试用例,使其 仅覆盖一个 尚未被覆盖的无效等价类 , 重复这一步,直到 所有的无效等价类 都被 覆盖 为止。
  • 原则2 是为了 把测试工作量减到最小
  • 原则3可把多个错误分开

4.1.3. 用等价类划分法设计测试用例的实例

某程序设计语言关于语法的规定
  • 标识符是以 字母 开头,后跟 字母数字 的任意组合而构成的。
    • 有效字符数为 \(8\) 个,最大字符数为 \(80\) 个。
  • 标识符必须 先说明再使用
  • 在同一说明语句中,标识符 至少 必须有 一个
建立的输入等价类表
输入数据 有效等价类 无效等价类
标识符个数 1个 \(^{(1)}\) ,多个 \(^{(2)}\) 0个 \(^{(3)}\)
标识符字符数 1~80个 \(^{(4)}\) 0个 \(^{(5)}\) ,>80个 \(^{(6)}\)
标识符组成 字母 \(^{(7)}\) ,数字 \(^{(8)}\) 非字母数字字符 \(^{(9)}\) ,保留字 \(^{(10)}\)
第一个字符 字母 \(^{(11)}\) 非字母 \(^{(12)}\)
标识符使用 先说明后使用 \(^{(13)}\) 未说明已使用 \(^{(14)}\)
覆盖所有等价类的测试用例
VAR x, T1234567 : REAL;
BEGIN x := 3.414; T1234567 := 2.732; ……
(1), (2), (4), (7), (8), (11), (13)
VAR : REAL; (3)
VAR x, : REAL; (5)
VAR T12345…… : REAL; (6) 多于80个字符
VAR T$ : CHAR; (9)
VAR GOTO : INTEGER; (10)
VAR 2T : REAL; (12)
VAR PAR : REAL;
BEGIN……
PAP := SIN(3.14 * 0.8) / 6;
(14)

4.2. 边界值分析

4.2.1. 边界值分析方法的考虑

边界值分析 也是一种 黑盒测试 方法,是对 等价类划分 方法的补充。

人们从长期的测试工作经验中得知, 大量的 错误 是发生在 输入输出 范围的 边界 上, 而不是在输入范围的内部。

  • 使用 边界值分析 方法设计 测试用例 ,首先应分析 边界 情况。
  • 通常 输入等价类输出等价类边界 是需要认真考虑的。
  • 应当选取
    • 正好等于 边界 的值、 刚刚大于 边界 的值、 刚刚小于 边界 的值
    • 来作为 测试数据 ,而不是选取等价类中的典型值或任意值作为测试数据。

4.2.2. 选择测试用例的原则

边界值分析方法选择测试用例的原则在很多方面与等价类划分方法类似。

  1. 如果输入数据规定了 值的范围 ,则应取刚达到这个范围的 边界的值 , 以及 刚刚超越这个范围边界的值 作为测试输入数据。
  2. 如果输入数据规定了 值的个数 ,则用 最大个数最小个数比最大个数多1比最小个数少1 的数作为测试数据。
  3. 根据规格说明的 每个输出数据,使用前面的原则1
  4. 根据规格说明的 每个输出数据,使用前面的原则2
  5. 如果程序的规格说明给出的 输入域输出域有序集合(如有序表、顺序文件等) , 则应选取集合的 第一个元素最后一个元素 作为测试用例。
  6. 如果程序中使用了一个 内部数据结构 ,则应当选择这个内部数据结构的 边界上的值 作为测试用例。
  7. 分析规格说明,找出 其他可能的边界条件

4.2.3. 应用边界值分析方法设计测试用例的实例

  • 程序的输入文件由一些包含80个字符的记录(卡片)组成。
  • 输入数据记录格式如图所示。

score-program-input-data-question.png

Figure 9: 学生考卷评分和成绩统计程序输入数据形式(试卷部分)

score-program-input-data-answer.png

Figure 10: 学生考卷评分和成绩统计程序输入数据形式(学生答卷部分)

记录可分为3组
  1. 标题 。这一组只有一个记录,其内容是成绩报告的名字。
  2. 各题的标准答案 。每个记录均在第80个字符处标以数字2。
    • 第1个记录 :第1~50题
      • 第1~3个字符 :试题数(取值为1~999)
      • 第10~59个字符 :第1~50题的标准答案(每个合法字符表示一个答案)
    • 第2个记录 :第51~100题
    • 第3个记录 :第101~150题
    • ……
  3. 学生的答卷 。每个记录均在第80个字符处标以数字3,每个学生的答卷在若干个记录中给出。 学生人数不超过200人,试题个数不超过999。
    • 某甲的第1个记录 :第1~50题
      • 第1~9个字符 :学生的学号
      • 第10~59个字符 :列出其所做的第1~50题的解答
    • 某甲的第2个记录 :第51~100题
    • ……
程序的输出有4个报告
  1. 按学号排列的成绩单 ,列出每个学生的成绩(百分制)、名次;
  2. 按学生成绩排序的成绩单
  3. 平均分数标准偏差的报告
  4. 试题分析报告 。按试题号排列,列出各题学生答对的百分比。
输入数据 测试用例
输入文件 【空输入文件】
标题 【没有标题记录】【标题只有一个字符】【标题有80个字符】
试题数 【试题数为1】【试题数为50】【试题数为51】【试题数为100】【试题数为999】【试题数为0】【试题数含有非数字字符】
标准答案记录 【没有标准答案记录,有标题】【标准答案记录多一个】【标准答案记录少一个】
学生人数 【0个学生】【1个学生】【200个学生】【201个学生】
学生答题 【某学生只有一个回答记录,但有两个标准答案记录】【该学生是文件中的第一个学生】【该学生是文件中的最后一个学生(记录数出错的学生)】
  【某学生有两个回答记录,但只有一个标准答案记录】【该学生是文件中的第一个学生(指记录数出错的学生)】【该学生是文件中的最后一个学生】
输出数据 测试用例
学生成绩 【所有学生的成绩都相等】【每个学生的成绩都互不相同】【部分(不是全体)学生的成绩相同(检查是否能按成绩正确排名次)】【有个学生得0分】【有个学生得100分】
输出报告 \(^{(1)}\) \(^{(2)}\) 【有个学生的学号最小(检查按学号排序是否正确)】【有个学生的学号最大(检查按学号排序是否正确)】【适当的学生人数,使产生的报告刚好印满一页(检查打印页数)】【学生人数使报告印满一页尚多出1人(检查打印换页)】
输出报告 \(^{(3)}\) 【平均成绩为100分(所有学生都得满分)】【平均成绩为0分(所有学生都得0分)】【标准偏差为最大值(有一半学生得0分,其他100分)】【标准偏差为0(所有学生得成绩都相等)】
输出报告 \(^{(4)}\) 【所有学生都答对了第一题】【所有学生都答错了第一题】【所有学生都答对了最后一题】【所有学生都答错了最后一题】【选择适当的试题数,使第四个报告刚好印满一页】【试题数使报告印满一页后,刚好剩下一题未打】

5. 软件测试的策略

通常情况下软件测试过程的4个步骤
  • 单元测试
  • 组装测试
  • 确认测试
  • 系统测试

software-testing-process.png

Figure 11: 软件测试的过程

5.1. 单元测试

单元测试(unit testing) 又称为 模块测试 , 是针对软件设计的最小单位 程序模块 进行 正确性检验 的测试工作。

单元测试的目的
  • 发现各模块内部可能存在的各种差错。

单元测试需要从程序的 内部结构 出发设计测试用例。 多个模块可以 平行地 独立进行单元测试。

单元测试的内容
  • 模块接口测试
  • 局部数据结构测试
  • 路径测试
  • 错误处理测试
  • 边界测试
进行单元测试时的辅助模块
  • 驱动模块(driver) :相当于被测模块的主程序。
  • 桩模块(stub) :也叫做存根模块,用以代替被测模块调用的子模块。

5.2. 组装测试

组装测试(integrated testing) 也叫做 集成测试联合测试 。 通常,在单元测试的基础上,需要将所有模块按照设计要求组装成为系统。

进行组装测试时需要考虑的问题
  • 在把各个模块连接起来的时候,穿越 模块接口的数据 是否会丢失;
  • 一个模块的功能 是否会对 另一个模块的功能 产生 不利的影响
  • 各个子功能组合起来,能否达到 预期要求的父功能
  • 全局数据结构 是否有问题;
  • 单个模块的误差累积 起来,是否会放大到不能接受的程度。

5.3. 确认测试

确认测试(validation testing) 又称 有效性测试 。 它的任务是 验证软件的有效性 , 即 验证软件的功能和性能及其他特性是否与用户的要求一致

在确认测试阶段需要做的工作
  • 进行有效性测试(黑盒测试)
  • 软件配置复查
  • \(\alpha\) 测试和 \(\beta\) 测试
    • \(\alpha\) 测试 是由一个用户在开发环境下进行的测试, 也可以是公司内部的用户在模拟实际操作环境下进行的测试。
    • \(\beta\) 测试 是由软件的多个用户在一个或多个用户的实际使用环境下进行的测试。 这些用户是与公司签订了支持产品预发行合同的外部客户,他们使用产品, 并愿意把所发现的错误信息反馈给开发者。
  • 验收测试(acceptance testing)
  • 确认测试的结果

5.4. 系统测试

系统测试(system testing) 是将通过 确认测试 的软件,作为整个 计算机系统的一个元素 , 与 计算机硬件外设某些支持软件数据人员 等其他 系统元素 结合在一起, 在 实际运行(使用) 环境下,对计算机系统进行 一系列的组装测试和确认测试

系统测试的目的
  • 通过与系统的需求定义做比较,发现 软件与系统定义不符合与之矛盾 的地方。

系统测试的测试用例 应根据 系统的需求规格说明书 设计,并在 实际使用 环境下运行。

5.5. 测试的类型

testing-types.png

Figure 12: 各测试步骤中的测试种类

6. 人工测试

人工测试 不要求在计算机上实际执行被测程序, 而是以一些 人工的模拟技术 和一些 类似动态分析所使用的方法 对程序进行 分析和测试

6.1. 静态分析

静态分析 是要对源程序进行 静态检验

通常采用的方法有
  • 生成各种引用表 :为了支持对源程序的静态分析;
  • 静态错误分析 :用于确定在源程序中是否有某类 错误危险 结构。
常用的几种引用表
  • 标号交叉引用表变量交叉引用表
  • 子程序、宏结构和函数表等价表常数表
静态错误分析的几种分类
  • 类型和单位分析
  • 引用分析
  • 表达式分析
  • 接口分析

6.2. 人工测试方法

静态分析 中进行 人工测试的主要方法桌面检查代码评审走查

经验表明,使用这种方法能够有效地发现30%~70%的 逻辑设计和编码错误

7. 自动化测试

7.1. 自动化测试与手工测试

自动化测试 就是使用 自动化测试工具其他手段 , 按照 测试工程师的预定计划 对软件进行 自动测试

目的
  • 减少手工测试的工作量提高测试效率 ,从而 提高软件产品的质量
基本原理
  • 首先识别软件中的 各个对象 ,记录下用户的 每一步操作 ,然后将这些操作转换为 测试脚本
适用情况
  • 回归测试压力测试并发测试强度测试系统调优
优点
  • 准确可靠高复用性永不疲劳重复测试节省时间
  • 能够 缩短测试周期节省人力资源
局限性
  • 缺乏创造性 ,难以发现新的缺陷;
  • 很难进行 界面用户体验 方面的测试;
  • 需要做大量的准备工作, 测试过程更复杂

自动化测试 不能 完全取代 手工测试 ,两者 互为补充 。 据统计, 自动化测试 能够找出约 30% 的缺陷, 大多数缺陷仍然需要依靠手工测试来发现

7.2. 脚本技术

测试脚本 是一组测试工具执行的 指令集合 , 既可以通过 录制测试的操作步骤 而产生,也可以直接 用脚本语言编写

脚本的几种分类
  • 线性脚本 是通过 录制手工测试过程 而得到的,只适合 简单的测试使用
  • 结构化脚本 是在 线性脚本 的基础上加入了 控制结构(顺序结构、选择结构和循环结构) 以及 函数调用功能 。结构化脚本具有较好的 可读性复用性 ,易于维护。
  • 数据驱动脚本 是将 测试脚本测试数据 分离,将测试数据存储在 独立的文件或数据库 中。
  • 关键字驱动脚本 是将 测试脚本中的通用功能 剥离出来,封装成 关键字

7.3. 自动化测试框架及测试流程

根据使用的脚本类型的不同可将自动化测试框架分为
  • 线性框架 :为录制/回放框架,通常不需要编写测试脚本,只需要录制一次测试过程, 在以后的测试中回放所录制的结果即可。
  • 数据驱动框架 :将测试数据和测试脚本分离开来,能够方便地使用不同的测试数据多次测试同一个功能或特性, 提高了脚本的复用性和维护性。
  • 关键字驱动框架 :使用关键字驱动脚本,提高了脚本的编写效率, 使得脚本更容易维护,同时关键字在可以在多个测试中复用。
从应用角度可将自动化测试框架分为
  • 单元测试框架
  • UI功能测试框架
  • 移动应用测试框架
  • API测试框架
  • ……

8. 调试

调试(debug) 也称 排错纠错 ,它是紧跟在测试之后要做的工作。

调试与测试的不同之处
  • 测试 在于 发现软件中有错 ,发现 异常软件运行的可疑之处
  • 调试 在于 为错误确切地定位 ,找到 出错的根源 ,并且 通过修改程序将其排除

8.1. 调试的步骤

  1. 针对测试提供的信息,分析错误的外部表现形式,确定程序出错的位置;
  2. 研究程序的相关部分,找出导致错误的内在原因;
  3. 修改相关的程序段,如果是设计导致的错误,则需修改相关的设计,以排除错误;
  4. 重复执行以前发现错误的测试,以确认:
    • 该错误确已通过修改而消除;
    • 这次修改并未引进新的错误。
  5. 如果重新测试表明修改无效,发生错误的现象仍然出现,则要撤销上述修改, 再次进行信息分析,实施上述过程,直至修改有效为止。

8.2. 调试工作需认真对待的问题

  1. 认真分析错误征兆是成功完成调试的关键;
  2. 目前已开发出一些商品化调试工具,但应将其当作调试工作的辅助手段, 它不可能代替人的思考和判断;
  3. 发现一个错误时需认真判断在其附近是否存在另外的错误, 规律表明,一些错误的出现有聚集现象;
  4. 务必防止一个错误的修改带来新的错误,回归测试一定不可少。

9. 课后作业

  1. 习题10.4 简要说明白盒测试和黑盒测试的区别。 如果认真做了两者之一,还需要再做另一种测试吗?
  2. 现在有一个档案管理系统,允许用户通过输入年月对档案文件进行检索, 系统对查询条件年月的输入限定为1990年1月-2049年12月, 并规定,日期由6位数字组成,前4位表示年,后2位表示月。 使用等价类划分法设计测试用例。

9.1. 第二题参考答案

根据需求进展分析,找出有哪些输入条件
  • 年份: [1990, 2049]
  • 月份: [01, 12]
  • 字符长度: 6位
  • 字符类型: 数字
画出等价类
输入条件 有效等价类 边界值分析 无效等价类
年份 [1990, 2049] (1) 1990, 2049 (12); 1989, 2050; 2016 <1990 (2), >2049 (3)
月份 [01, 12] (4) 01, 12 (13); 00, 13; 11 <01 (5), >12 (6)
字符长度 6位 (7) 6; 5, 7; 6 <6 (8), >6 (9)
字符类型 数字 (10)   非数字 (11)
为每个等价类规定一个唯一编号
转换成测试用例
  • 有效等价类用例
    • 用例1:201611 (1) (4) (7) (10)
  • 无效等价类用例
    • 用例2:198911 (2)
    • 用例3:205011 (3)
    • 用例4:201600 (5)
    • 用例5:201613 (6)
    • 用例6:20161 (8)
    • 用例7:2016113 (9)
    • 用例8:20161a/abcedf (11)
根据边界值分析法分析后补充测试用例
  • 用例9:199001 (12)
  • 用例10:204912 (13)