软件测试方法
Created: 2024-03-05 Tue 16:42
软件测试 是为了 发现错误 而 执行程序 的过程。
或者说, 软件测试
- 是根据软件开发各阶段的 规格说明 和 程序的内部结构 而精心设计一批 测试用例 (即 输入数据 及其 预期的输出结果 ),
- 并利用这些 测试用例 去 运行程序 ,以 发现程序错误 的过程。
软件测试 并不等于程序测试。软件测试应贯穿于软件 定义 与 开发 的 整个期间 。
据美国一家公司的统计表明,在查找出的软件错误中,属于 需求分析 和 软件设计 的错误约占 \(64\%\) , 属于程序编写的错误仅占 \(36\%\) 。
Figure 1: 测试信息流
Figure 2: 软件测试与软件开发过程的关系
用黑盒测试发现程序中的错误,必须在 所有 可能的 输入条件 和 输出条件 中确定 测试数据 , 检查程序是否都能产生正确的输出。
假设一个 程序 \(P\) 有 输入量 \(X\) 和 \(Y\) 及 输出量 \(Z\) ,在字长为 32位 的计算机上运行。 如果 \(X, Y\) 只取整数,考虑把所有的 \(X, Y\) 值都作为测试数据,按 黑盒 方法进行 穷举测试 。
%%{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"]
Figure 3: 黑盒子
对于一个具有 多重选择 和 循环嵌套 的程序, 独立路径 数目可能是天文数字。
穷举测试 由于 工作量过大 、 需要的时间过长 ,实施起来是 不现实 的。
逻辑覆盖 是以程序内部的 逻辑结构 为基础的设计测试用例的技术,它属于 白盒测试 。
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)\)
语句覆盖 是设计若干个测试用例,运行被测程序,使得 每一个可执行语句至少执行一次 。
测试用例的设计格式如下:
【输入的 \((A, B, X)\) ,输出的 \((A, B, X)\) 】
参与人数 | 0 |
---|---|
A | 0 |
B | 0 |
C | 0 |
D | 0 |
判定覆盖 是设计若干个测试用例,运行被测程序,使得 程序中每个判断的取真分支和取假分支至少经历一次 。
判定覆盖又称为 分支覆盖 。
条件覆盖 是设计若干个测试用例,运行被测程序,使得 程序中每个判断的每个条件的可能取值至少执行一次 。
测试用例 | 通过路径 | 条件取值 | 覆盖分支 |
---|---|---|---|
\([(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\) |
测试用例 | 通过路径 | 条件取值 | 覆盖分支 |
---|---|---|---|
\([(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, 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, 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, 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}\) |
控制流图 是描述程序的 控制流 的一种图示方法。
Figure 5: 控制流图的各种图形符号
Figure 6: 程序流程图与对应的控制流图
当判断中的条件表达式是 复合条件 时,需要改复合条件为一系列只有 单个条件 的 嵌套 的判断。
Figure 7: 复合逻辑下的控制流图
对于给定的 控制流图 \(G\) ,
从程序的 环路复杂性 可导出程序基本路径集合中的 独立路径数 。
独立路径 是指包括一组 以前没有处理 的 语句 或 条件 的一条路径。
Figure 8: 控制流图
等价类划分 是一种典型的 黑盒测试 方法,也是一种非常实用的重要测试方法。
等价类 是指每个 输入域 的 子集合 , 在该 子集合 中, 各个输入数据对于揭露程序中的错误都是等效的 。
因此,可以把 全部可供输入的数据 合理划分为 若干等价类 , 在每一个等价类中取一个数据作为测试的输入, 这样就可以 用少量代表性测试数据,达到测试的要求 。
在设计测试用例时,要同时考虑 有效等价类 和 无效等价类 。 软件不能都只接受合理的数据,还要经受意外的考验, 检验出无效的或不合理的数据,这样的软件测试才是全面性的 。
在确定了等价类之后,建立 等价类表 ,列出所有划分出的等价类:
输入数据 | 有效等价类 | 无效等价类 |
---|---|---|
…… | …… | …… |
…… | …… | …… |
- 原则2 是为了 把测试工作量减到最小 ,
- 原则3 则 可把多个错误分开 。
输入数据 | 有效等价类 | 无效等价类 |
---|---|---|
标识符个数 | 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) |
边界值分析 也是一种 黑盒测试 方法,是对 等价类划分 方法的补充。
人们从长期的测试工作经验中得知, 大量的 错误 是发生在 输入 或 输出 范围的 边界 上, 而不是在输入范围的内部。
边界值分析方法选择测试用例的原则在很多方面与等价类划分方法类似。
Figure 9: 学生考卷评分和成绩统计程序输入数据形式(试卷部分)
Figure 10: 学生考卷评分和成绩统计程序输入数据形式(学生答卷部分)
输入数据 | 测试用例 |
---|---|
输入文件 | 【空输入文件】 |
标题 | 【没有标题记录】【标题只有一个字符】【标题有80个字符】 |
试题数 | 【试题数为1】【试题数为50】【试题数为51】【试题数为100】【试题数为999】【试题数为0】【试题数含有非数字字符】 |
标准答案记录 | 【没有标准答案记录,有标题】【标准答案记录多一个】【标准答案记录少一个】 |
学生人数 | 【0个学生】【1个学生】【200个学生】【201个学生】 |
学生答题 | 【某学生只有一个回答记录,但有两个标准答案记录】【该学生是文件中的第一个学生】【该学生是文件中的最后一个学生(记录数出错的学生)】 |
【某学生有两个回答记录,但只有一个标准答案记录】【该学生是文件中的第一个学生(指记录数出错的学生)】【该学生是文件中的最后一个学生】 |
输出数据 | 测试用例 |
---|---|
学生成绩 | 【所有学生的成绩都相等】【每个学生的成绩都互不相同】【部分(不是全体)学生的成绩相同(检查是否能按成绩正确排名次)】【有个学生得0分】【有个学生得100分】 |
输出报告 \(^{(1)}\) \(^{(2)}\) | 【有个学生的学号最小(检查按学号排序是否正确)】【有个学生的学号最大(检查按学号排序是否正确)】【适当的学生人数,使产生的报告刚好印满一页(检查打印页数)】【学生人数使报告印满一页尚多出1人(检查打印换页)】 |
输出报告 \(^{(3)}\) | 【平均成绩为100分(所有学生都得满分)】【平均成绩为0分(所有学生都得0分)】【标准偏差为最大值(有一半学生得0分,其他100分)】【标准偏差为0(所有学生得成绩都相等)】 |
输出报告 \(^{(4)}\) | 【所有学生都答对了第一题】【所有学生都答错了第一题】【所有学生都答对了最后一题】【所有学生都答错了最后一题】【选择适当的试题数,使第四个报告刚好印满一页】【试题数使报告印满一页后,刚好剩下一题未打】 |
Figure 11: 软件测试的过程
单元测试(unit testing) 又称为 模块测试 , 是针对软件设计的最小单位 程序模块 进行 正确性检验 的测试工作。
单元测试需要从程序的 内部结构 出发设计测试用例。 多个模块可以 平行地 独立进行单元测试。
组装测试(integrated testing) 也叫做 集成测试 或 联合测试 。 通常,在单元测试的基础上,需要将所有模块按照设计要求组装成为系统。
确认测试(validation testing) 又称 有效性测试 。 它的任务是 验证软件的有效性 , 即 验证软件的功能和性能及其他特性是否与用户的要求一致 。
系统测试(system testing) 是将通过 确认测试 的软件,作为整个 计算机系统的一个元素 , 与 计算机硬件 、 外设 、 某些支持软件 、 数据 和 人员 等其他 系统元素 结合在一起, 在 实际运行(使用) 环境下,对计算机系统进行 一系列的组装测试和确认测试 。
系统测试的测试用例 应根据 系统的需求规格说明书 设计,并在 实际使用 环境下运行。
Figure 12: 各测试步骤中的测试种类
人工测试 不要求在计算机上实际执行被测程序, 而是以一些 人工的模拟技术 和一些 类似动态分析所使用的方法 对程序进行 分析和测试 。
静态分析 是要对源程序进行 静态检验 。
静态分析 中进行 人工测试的主要方法 有 桌面检查 、 代码评审 和 走查 。
经验表明,使用这种方法能够有效地发现30%~70%的 逻辑设计和编码错误 。
自动化测试 就是使用 自动化测试工具 或 其他手段 , 按照 测试工程师的预定计划 对软件进行 自动测试 ,
自动化测试 不能 完全取代 手工测试 ,两者 互为补充 。 据统计, 自动化测试 能够找出约 30% 的缺陷, 大多数缺陷仍然需要依靠手工测试来发现 。
测试脚本 是一组测试工具执行的 指令集合 , 既可以通过 录制测试的操作步骤 而产生,也可以直接 用脚本语言编写 。
调试(debug) 也称 排错 或 纠错 ,它是紧跟在测试之后要做的工作。
输入条件 | 有效等价类 | 边界值分析 | 无效等价类 |
---|---|---|---|
年份 | [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) |