知识点
白盒测试概述
- 白盒测试基于程序内部逻辑结构进行测试,关注程序语句、路径、变量状态等。
- 单元测试主要采用白盒测试方法,辅以黑盒测试方法。
- 程序内部结构示意图
白盒测试关注的对象
- 源代码
- 检验代码规范性,查找逻辑、内存管理、数据定义和使用缺陷。
- 程序结构
- 使用图表找到设计缺陷,评价程序执行效率。
白盒测试方法分类
- 控制流分析技术(基本路径测试法)
- 逻辑覆盖法
- 语句覆盖(Statement Coverage, SC)
- 判定覆盖(Decision Coverage, DC)
- 条件覆盖(Condition Coverage, CC)
- 条件判定组合覆盖(Condition/Decision Coverage, CDC)
- 多条件覆盖(Multiple Condition Coverage, MCC)
- 修正条件判定覆盖(Modified Condition Decision Coverage, MCDC)
控制流分析技术
- 解决程序结构复杂性问题,衡量复杂程度,测试执行流程变化因素,确保测试效率。
- 要分析解决的问题:
- 什么因素导致程序结构变得复杂?
- 如何衡量程序结构的复杂程度?
- 控制程序执行流程发生变化的主要因素是什么?
- 如何测试这些因素,并确保测试的效率?
- 常见程序结构:判定结点导致结构复杂,风险大小分类。
控制流分析内容
- 判定结点:关注固有复杂性,使用逻辑覆盖测试。焦点:判定表达式
- 判定结构与循环结构:关注对执行路径的影响,使用独立路径测试。焦点:路径
- 循环结构本身:关注复杂性,使用基于数据的静态分析。焦点:循环体
白盒测试用例设计及应用
- 语句覆盖:其基本思想是设计若干个测试用例,运行被测程序,确保程序中每条可执行语句至少执行一次。
- 语句覆盖法案例:设计测试用例,满足不同覆盖标准。
- 单元设计要求:设计一个方法,输入两个整型参数x,y,当x小于5或者y=5时将x和y的和作为结果返回;否则,将x和y的商作为结果返回。
-
public class MyClass { public int computing(int x, int y) { int result; if (x < 5 && y == 5) { result = x + y; } else { result= x / y; } return result; }
-
序号
X
y
预期结果
X<5
Y==5
x < 5 && y == 5
路径
1
2
5
7
T
T
T
2
6
6
1
F
F
F
判定覆盖
- 定义:确保程序中每个判定表达式都至少获得一次“真”值和一次“假”值,使每个分支至少执行一次。
- 案例:给出了一个
MyClass
类中computing
方法的判定覆盖测试用例设计。public class MyClass { public int computing(int x, int y) { int result; if (x < 5 || y == 5) { result = x + y; } else { result= x /y; } return result; }
序号
x
y
预期结果
x<5
y==5
x < 5 || y == 5
路径
1
2
6
8
T
F
T
2
6
6
1
F
F
F
条件覆盖
- 定义:确保程序中每个判定表达式中的每个条件的所有可能取值(真或假)至少出现一次。
- 案例:提供了
MyClass
类中computing
方法的条件覆盖测试用例设计,并指出该测试用例集满足条件覆盖但不满足判定覆盖。这个测试用例集满足条件覆盖的要求,但是不满足判定覆盖的要求public int computing(int x, int y) { int result; if (x < 5 || y == 5) { result = x + y; } else { result= x /y; } return result; }
序号
x
y
预期结果
x<5
y==5
x < 5 || y == 5
路径
1
2
1
3
T
F
T
2
6
5
1
F
T
T
判定/条件覆盖
- 定义:同时满足判定覆盖和条件覆盖的要求,即每个判定表达式及其条件的所有可能组合都要至少执行一次。并且每个判定本身的判定结果(真/假)也至少出现一次。
public int computing(int x, int y) { int result; if (x < 5 || y == 5) { result = x + y; } else { result= x /y; } return result; }
序号
x
y
预期结果
x<5
y==5
x < 5 || y == 5
路径
1
2
1
3
T
F
T
2
6
5
1
F
T
T
3
6
1
1
F
F
F
组合覆盖
- 定义:确保程序中每个判定表达式的所有条件取值组合至少出现一次,这是最强的逻辑覆盖标准,确保所有可能的执行路径都被测试。
public int computing(int x, int y) { int result; if (x < 5 || y == 5) { result = x + y; } else { result= x /y; } return result; }
序号
x
y
预期结果
x<5
y==5
x < 5 || y == 5
路径
1
2
1
3
T
F
T
2
6
5
1
F
T
T
3
6
1
1
F
F
F
4
1
5
0
T
T
T
基本路径测试法
- 定义:设计测试用例以覆盖程序控制流图中的所有基本路径。
- 控制流图:展示程序执行的流向,包括决策点和路径。
- 环路复杂性:使用McCabe复杂性度量来确定程序基本路径集合中的独立路径条数,这是确保每个可执行语句至少执行一次所需的测试用例数目的上界。
- 程序的环路复杂性即McCabe复杂性度量,在进行程序的基本路径测试时,从程序的环路复杂性可导出程序基本路径集合中的独立路径条数,这是确保程序中每个可执行语句至少执行一次所必须的测试用例数目的上界。
- 独立路径是指包括一组以前没有处理过的语句或条件的一条路径。从控制流图来看,一条独立路径是至少包含有一条在其他独立路径中从未有过的边的路径。
- (b)所示的控制流图中,一组独立的路径如下:
- 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
- (1)以详细的设计或源代码为基础,导出程序的控制流程图
- (2)计算控制流图G的环路复杂性V(G)
- V(G)=区域数
- V(G)=判断节点数+1
- V(G)=边的个数-节点个数+2
- (3)导出独立路径
- (4)设计测试用例,确保每条路径都被执行
基本路径覆盖测试用例设计步骤
- 基于详细设计或源代码,导出程序的控制流程图。
- 计算控制流图G的环路复杂性V(G),公式为V(G) = e - n + 2,其中e是边数,n是节点数。
- 导出独立路径集合。
- 设计测试用例,确保每条路径都被执行。
基本路径覆盖案例
- 案例分析:展示了一个将正整数分解为质因数的Java程序,并画出程序控制流图。
-
int k=2; 1 String rs=n+"="; 2 while(k<=n){ 3 if(k==n){ 4 rs=rs+n; break; }else{ 5 if(n%k==0){ rs=rs+k+"*"; 6 n=n/k; }else{ 7 k++; 8 }}} return rs;
-
- 环形复杂度计算:示例计算了程序的环形复杂度V(G)。
- (2)计算环形复杂度: V(G)=9-8+2=1+2=3
- 将V(G)定义为: V(G)=e-n+2 这里,e是控制流图的边数,n是控制流图的节点数。我们还可以用如下两个方法计算环形复杂度:
- V(G)=区域数
- V(G)=判定节点数+1 这里,区域是指由边包围起来的形状,图中没有被边包围的部分也算一个区域。判定节点是有多个边以它作为起点的节点。
- 独立路径找出:示例找出了程序的一组独立路径。
- (3)找出一组独立路径(基本路径集合):
- 路径1:1-2-3-4
- 路径2:1-2-3-5-6-8-2-3-4
- 路径3:1-2-3-5-7-8-2-3-4
- 测试用例设计:基于基本路径设计测试用例,覆盖所有路径。
实验
一 实验目的:
1、理解白盒测试的定义;
2、掌握控制流分析方法;
3、掌握白盒测试用例设计方法及应用。
二 实验环境
1、JDK8.0或以上;
2、Itellij IDEA集成开发环境;
3、Maven构建工具。
三 实验准备
1、掌握JUnit测试框架的基本使用;
2、具备Java编程基础;
3、安装及配置好测试环境。
四 实验内容
(一)阅读以下需求描述,并完成以下内容。
现有一网上蛋糕商城购物平台。用户可在商城首页或商品列表添加商品到购物车中。当商品添加入购物车后,订单提交的流程如下:
(1)点击进入购物车页面;
(2)在购物车页面添加或减少或删除商品;
(3)点击提交订单按钮。
(4)在订单页面,确认收货地址,选择支付方式,点击确认订单按钮。
以下为网上蛋糕商城订单提交功能的部分伪代码实现。
if(登陆成功){
if(微信支付 OR 支付宝支付){
if(交易金额<=余额) then
提示“支付成功”
else
提示“余额不足”
}
}
else
提示“请登录”
endif
实验内容:
(1)请根据以上蛋糕商城订单提交流程及伪码画出其程序流程图。
- 开始:流程开始。
- 登录检查:检查用户是否登录成功。
- 支付方式检查:如果登录成功,检查用户选择的支付方式是否为微信支付或支付宝支付。
- 余额检查:如果支付方式正确,检查交易金额是否小于或等于用户余额。
- 支付成功:如果交易金额小于或等于余额,提示“支付成功”。
- 余额不足:如果交易金额大于余额,提示“余额不足”。
- 登录提示:如果用户未登录,提示“请登录”。
- 结束:流程结束。
(2)请根据你所画的程序流程图设计满足语句覆盖的测试用例。
编号 | 测试用例名称 | 输入数据 | 预期结果 | 测试路径 | ||||
登录状态 | 微信支付 | 支付宝支付 | 交易金额 | 余额 | ||||
语句覆盖意味着我们需要确保伪代码中的每一条语句至少执行一次。
(3)请根据你所画的程序流程图设计满足判定覆盖的测试用例。
编号 | 测试用例名称 | 输入数据 | 预期结果 | 测试路径 | ||||
登录状态 | 微信支付 | 支付宝支付 | 交易金额 | 余额 | ||||
确保程序中每个判定表达式的每个分支至少执行一次。根据伪代码,存在两个判定表达式:登录状态检查和支付方式及余额检查。
编号 | 测试用例名称 | 登录状态 | 微信支付 | 支付宝支付 | 交易金额 | 余额 | 预期结果 | 测试路径 |
---|---|---|---|---|---|---|---|---|
1 | 登录成功_支付成功 | 成功 | 否 | 是 | <= 余额 | 充足 | 支付成功 | 检查登录 -> 选择支付宝 -> 检查余额 -> 支付成功 |
2 | 登录成功_余额不足 | 成功 | 否 | 是 | > 余额 | 不足 | 余额不足 | 检查登录 -> 选择支付宝 -> 检查余额 -> 余额不足 |
3 | 未登录 | 失败 | - | - | - | - | 请登录 | 直接检查登录状态 -> 未登录 |
4 | 登录成功_微信支付_支付成功 | 成功 | 是 | 否 | <= 余额 | 充足 | 支付成功 | 检查登录 -> 选择微信 -> 检查余额 -> 支付成功 |
5 | 登录成功_微信支付_余额不足 | 成功 | 是 | 否 | > 余额 | 不足 | 余额不足 | 检查登录 -> 选择微信 -> 检查余额 -> 余额不足 |
(4)请根据你所画的程序流程图设计满足条件覆盖的测试用例。
编号 | 测试用例名称 | 输入数据 | 预期结果 | 测试路径 | ||||
登录状态 | 微信支付 | 支付宝支付 | 交易金额 | 余额 | ||||
条件覆盖(Condition Coverage)要求确保程序中每个判定表达式的每个条件都独立地至少为真和为假一次。根据提供的伪代码,主要有两个条件需要考虑:
登录成功
:这个条件决定了用户是否能够进行支付操作。微信支付 OR 支付宝支付
:这个条件检查用户选择的支付方式是否被支持。交易金额 <= 余额
:这个条件检查用户的账户余额是否足够支付订单。-
编号 测试用例名称 登录状态 微信支付 支付宝支付 交易金额 余额 预期结果 测试路径 1 条件1真_条件2真_条件3真 成功 是 否 <= 余额 充足 支付成功 条件1真 -> 条件2真(微信) -> 条件3真 2 条件1真_条件2假_条件3真 成功 否 是 <= 余额 充足 支付成功 条件1真 -> 条件2假(支付宝) -> 条件3真 3 条件1真_条件2真_条件3假 成功 是 否 > 余额 不足 余额不足 条件1真 -> 条件2真(微信) -> 条件3假 4 条件1真_条件2假_条件3假 成功 否 是 > 余额 不足 余额不足 条件1真 -> 条件2假(支付宝) -> 条件3假 5 条件1假 失败 - - - - 请登录 条件1假
(5)请根据你所画的程序流程图设计满足组合覆盖的测试用例。
编号 | 测试用例名称 | 输入数据 | 预期结果 | 测试路径 | ||||
登录状态 | 微信支付 | 支付宝支付 | 交易金额 | 余额 | ||||
组合覆盖(Multiple Condition Coverage)要求设计测试用例,使得判定点中所有条件的所有可能组合至少执行一次。对于给出的伪代码,我们有两个主要的判定点:
- 登录状态(成功或失败)
- 支付方式和余额检查(微信支付或支付宝支付,交易金额小于等于余额或交易金额大于余额)
-
编号 测试用例名称 登录状态 微信支付 支付宝支付 交易金额 余额 预期结果 测试路径 1 成功登录_微信支付_支付成功 成功 是 否 <= 余额 充足 支付成功 登录成功 -> 选择微信支付 -> 交易金额 <= 余额 -> 支付成功 2 成功登录_微信支付_余额不足 成功 是 否 > 余额 不足 余额不足 登录成功 -> 选择微信支付 -> 交易金额 > 余额 -> 余额不足 3 成功登录_支付宝支付_支付成功 成功 否 是 <= 余额 充足 支付成功 登录成功 -> 选择支付宝支付 -> 交易金额 <= 余额 -> 支付成功 4 成功登录_支付宝支付_余额不足 成功 否 是 > 余额 不足 余额不足 登录成功 -> 选择支付宝支付 -> 交易金额 > 余额 -> 余额不足 5 未登录_任何支付方式 失败 - - - - 请登录 未登录 -> 显示请登录提示
(6)请将程序流程图转化为控制流图,并计算其环形复杂度,写出基本路径集合。
环路复杂度为:
基本路径为:
1.
2
3
4
- 节点1:开始
- 节点2:登录检查
- 节点3:支付方式检查(微信支付或支付宝支付)
- 节点4:余额检查(交易金额 <= 余额)
- 节点5:提示“支付成功”
- 节点6:提示“余额不足”
- 节点7:提示“请登录”
- 节点8:结束
边的情况如下:
- 从节点2到节点3或节点7,取决于登录是否成功。
- 从节点3到节点4或节点6,取决于支付方式检查结果。
- 从节点4到节点5,如果余额足够。
- 从节点4到节点7,如果余额不足。
- 节点6和5分别跳转到节点8。
根据上述信息,我们可以计算环路复杂度:
- 𝐸E = 6(边的数量)
- 𝑁N = 5(节点的数量,不包括开始和结束)
𝑉(𝐺)=6−5+2𝑃=3V(G)=6−5+2P=3
因为 𝑃P 通常为1,所以环路复杂度 𝑉(𝐺)V(G) 为3。
基本路径集合包括:
- 路径1:开始 -> 登录检查 -> 请登录 -> 结束(用户未登录)
- 路径2:开始 -> 登录检查 -> 支付方式检查 -> 余额检查 -> 提示“支付成功” -> 结束(用户登录且支付成功)
- 路径3:开始 -> 登录检查 -> 支付方式检查 -> 提示“余额不足” -> 结束(用户登录但余额不足)
五 实验总结
(1)是什么导致了程序的复杂性?对代码进行覆盖率测试时,主要关注点在哪几个方面?
- 程序规模:程序越大,包含的代码行数越多,逻辑分支和条件判断也越多,导致复杂性增加。
- 条件逻辑:程序中包含的if-else语句、switch-case语句、循环和循环嵌套等条件逻辑增加了程序的复杂性。
- 模块间交互:程序中不同模块或组件之间的交互和依赖关系增加了理解和维护的难度。
- 数据结构的使用:复杂的数据结构和算法实现增加了程序逻辑的复杂度。
- 多线程和并发:涉及多线程和并发的程序需要处理同步、竞态条件等问题,增加了程序的复杂性。
- 配置和外部因素:程序需要根据不同环境或配置进行调整,外部系统接口的复杂性也会影响程序。
对代码进行覆盖率测试时,主要关注以下几个方面:
- 语句覆盖(Statement Coverage):确保程序中每一行代码都被执行至少一次。
- 判定覆盖(Decision Coverage):确保程序中的每个判定点(如if语句、循环条件)的所有分支都被执行至少一次。
- 条件覆盖(Condition Coverage):确保程序中每个判定点内的所有条件表达式的所有可能结果(真/假)都被测试到。
- 路径覆盖(Path Coverage):确保程序中所有可能的执行路径都被执行至少一次。
- 条件判定组合覆盖(Condition/Decision Coverage, CDC):确保每个判定的所有条件组合都被测试到。
- 多条件覆盖(Multiple Condition Coverage, MCC):确保所有涉及多个条件的判定表达式的所有条件值组合都被测试到。
- 修正条件判定覆盖(Modified Condition/Decision Coverage, MCDC):确保每个条件能够独立影响判定的结果,即改变一个条件的值可以独立地改变判定的结果。
- 异常覆盖(Exception Coverage):确保程序中的异常处理路径被测试到,以验证异常流程的正确性。
- 边界值覆盖:测试输入或运行时的边界条件,确保程序在边界情况下的行为符合预期。
个人思考
实验中并没有展现出这几种方法之间有什么区别,感觉并不是一个有用的例子去帮助我们理解这几种方法的内涵以及具体使用有什么区别,写出来的测试例子也是一样的,需要思考在什么例子下,这几种方法写出来的测试例子会不一样。