软件测试 白盒测试思想 蓝桥杯
白盒测试思想
白盒测试用来设计测试用例,主要考察点分为逻辑覆盖法和循环覆盖法。
这里先介绍一下逻辑覆盖和覆盖率
逻辑覆盖:对代码中所有的逻辑均需测试真假和假值取得情况。
覆盖率:
覆盖率
=
(至少被执行一次的
i
t
e
m
数
)
/
i
t
e
m
的总数
覆盖率 =(至少被执行一次的item数)/item的总数
覆盖率=(至少被执行一次的item数)/item的总数
但是要说的是不清楚蓝桥杯是按照满测来判断对错还是按照覆盖率来判断,反正尽量在规定条数完成对于覆盖法就好
逻辑覆盖法
语句覆盖:基于每条语句的测试,语句覆盖相对简单,在测试的时候考虑每一个语句都进行过就好
判定/分支覆盖:设计测试用例时,每个判定节点(每个if语句)判断一次,判定覆盖虽然可能没有语句覆盖设计测试用例简单,但是很好判断是否进行了判定覆盖,把所有的分支节点做一个标记,然后判断是否走过即可。
条件覆盖:设计测试用例时,每个条件(if语句中的每种判断)执行一次,条件覆盖在写测试用例的时候一定要注意,对于条件覆盖不能只看判定语句(其实主要想说的是Java会短路,千万不要认为走过了这个判定语句就好)
判定条件覆盖:判定+条件,这个就是上面两种的结合
条件组合覆盖:使每个判定中条件的各种可能组合都至少执行一次
路径覆盖:每一种路径的所有可能组合的路径情况
基本路径覆盖:根据计算出来的覆盖条数设计,有三种方法,分别是计算流图中的区域、流图中边数减节点数加二、判定节点数加一``
循环覆盖法
在循环测试中需要注意测试条数
简单循环:
根据蓝桥云课的题目检测来判断检测点为:
跳过循环,通过一次循环,通过两次循环,通过m次循环(一般是一条,具体情况具体分析),n次循环(即最多循环次数),n-1次循环,n+1次循环
嵌套循环:
串接循环:
非结构循环:
测试不了!!!
--------逻辑覆盖例题--------
被测代码
public static int test(int a,int b,int c) {
int result = 0;
if(a == 0 || b > 2) {
result = b - a;
}
if(a > 0 && c > 0 ) {
result = c * a;
}
return result;
}
流程图
语句覆盖
测试用例编号 | 输入数据 | 预期结果 | 语句覆盖情况 |
---|---|---|---|
testcase_01 | a = 1 , b = 3 , c = 9 | result = 9 | 覆盖语句 1,2,4,3,5, 6 |
判定/分支覆盖
测试用例编号 | 输入数据 | 预期结果 | 分支覆盖情况 | 语句覆盖情况 |
---|---|---|---|---|
testcase_01 | a = 0 , b = 5 , c = 9 | result = 5 | 覆盖判断语句 ② 的真分支和判断语句 ③ 的假分支 | 1,2,4,3,6 |
testcase_02 | a = 5 , b = -2 , c = 3 | result = 15 | 覆盖判断语句 ② 的假分支和判断语句 ③ 的真分支 | 1,2,3,5,6 |
条件覆盖
条件 | 取值 | 标识 |
---|---|---|
a == 0 | 真 | Y1 |
a == 0 | 假 | N1 |
b > 2 | 真 | Y2 |
b > 2 | 假 | N2 |
a > 0 | 真 | Y3 |
a > 0 | 假 | N3 |
c > 0 | 真 | Y4 |
c > 0 | 假 | N4 |
测试用例编号 | 输入数据 | 预期结果 | 条件覆盖情况 | 分支覆盖情况 |
---|---|---|---|---|
testcase_01 | a = 0 , b = 5 , c = 9 | result = 5 | Y1、Y2、N3、Y4 | 判断语句 ② 的真分支和③ 的假分支 |
testcase_02 | a = 5 , b = 1 , c = -3 | result = 0 | N1、N2、Y3、N4 | 判断语句 ② 的假分支和 ③ 的假分支 |
这个例子真的很好,从上面这个覆盖可以很轻易的看出来缺点,如果为与分支则可能测试不到所有分支
判定条件覆盖
判断语句:
判断语句 | 取值 | 标识 |
---|---|---|
a == 0 or b > 2 | 真 | T1 |
a == 0 or b > 2 | 假 | F1 |
a > 0 and c > 0 | 真 | T2 |
a > 0 and c > 0 | 假 | F2 |
条件:
条件 | 取值 | 标识 |
---|---|---|
a == 0 | 真 | Y1 |
a == 0 | 假 | N1 |
b > 2 | 真 | Y2 |
b > 2 | 假 | N2 |
a > 0 | 真 | Y3 |
a > 0 | 假 | N3 |
c > 0 | 真 | Y4 |
c > 0 | 假 | N4 |
测试用例编号 | 输入数据 | 预期结果 | 条件覆盖情况 | 分支覆盖情况 |
---|---|---|---|---|
testcase_01 | a = 0 , b = 5 , c = 0 | result = 5 | Y1、Y2、N3、N4 | T1、F2 |
testcase_02 | a = 5 , b = 1 , c = 3 | result = 15 | N1、N2、Y3、Y4 | F1、T2 |
这里有一个小诀窍,每次判断语句中跑全真和全假就可以达到判定条件
条件组合
条件 | 取值 | 标识 |
---|---|---|
a == 0 | 真 | Y1 |
a == 0 | 假 | N1 |
b > 2 | 真 | Y2 |
b > 2 | 假 | N2 |
a > 0 | 真 | Y3 |
a > 0 | 假 | N3 |
c > 0 | 真 | Y4 |
c > 0 | 假 | N4 |
根据条件组合覆盖法的定义,我们需要设计一些测试用例分别使判断语句 ② 中两个条件的四种组合及判断语句 ③ 中两个条件的四种组合都至少执行一次,如下表所示:
组合编号 | 判断语句 | 条件组合 |
---|---|---|
1 | a == 0 or b > 2 | Y1 + Y2 |
2 | a == 0 or b > 2 | Y1 + N2 |
3 | a == 0 or b > 2 | N1 + Y2 |
4 | a == 0 or b > 2 | N1 + N2 |
5 | a > 0 and c > 0 | Y3 + Y4 |
6 | a > 0 and c > 0 | Y3 + N4 |
7 | a > 0 and c > 0 | N3 + Y4 |
8 | a > 0 and c > 0 | N3 + N4 |
测试用例编号 | 输入数据 | 预期结果 | 条件组合覆盖 | 条件覆盖 | 路径覆盖 |
---|---|---|---|---|---|
testcase_01 | a = 0 , b = 3 , c = 1 | result = 3 | 组合 1 、组合 7 | Y1、Y2、N3、Y4 | B - C |
testcase_02 | a = 0 , b = 1 , c = 0 | result = 1 | 组合 2 、组合 8 | Y1、N2、N3、N4 | B - C |
testcase_03 | a = 1 , b = 5 , c = 3 | result = 3 | 组合 3 、组合 5 | N1、Y2、Y3、Y4 | B - D |
testcase_04 | a = 2 , b = 0 , c = -1 | result = 1 | 组合 4 、组合 6 | N1、N2、Y3、N4 | A - C |
通过对上表的分析我们可以发现 :条件组合覆盖可以使程序判断断语句中的条件组合都至少被执行一次,但是,满足了条件组合覆盖也不能保证所有的路径都已经得到覆盖
路径覆盖
测试用例编号 | 输入数据 | 预期结果 | 路径覆盖情况 |
---|---|---|---|
testcase_01 | a = -2 , b = 1 , c = 9 | result = 0 | 路径 1 :A - C |
testcase_02 | a = 5 , b = -2 , c = 3 | result = 15 | 路径 2 :A - D |
testcase_03 | a = 0 , b = 3 , c = 3 | result = 3 | 路径 3 :B - C |
testcase_04 | a = 1 , b = 5 , c = 9 | result = 9 | 路径 4 :B - D |
基本路径覆盖
(这个单独放这里是因为这边需要计算环路复杂度,其他的测试方法不需要)
计算环路复杂度(这里就不展开说了):蓝桥杯直通国赛班(软件测试组) - 【练一练】基本路径覆盖 - 蓝桥云课 (lanqiao.cn)
测试用例编号 | 输入数据 | 预期结果 | 路径基本覆盖情况 |
---|---|---|---|
testcase_01 | a = 0 , b = 1 , c = 9 | result = 1 | 路径 1 |
testcase_02 | a = 0 , b = 3 , c = 5 | result = 3 | 路径 2 |
testcase_03 | a = -2 , b = 1 , c = 3 | result = 0 | 路径 3 |
testcase_04 | a = 1 , b = 0 , c = -1 | result = 0 | 路径 4 |
testcase_05 | a = 5 , b = -3 , c = 2 | result = 10 | 路径 5 |
--------循环覆盖例题--------
简单循环测试
测试代码
public static int getFactorial(Integer num) {
int result = 0;
if (num >= 1 && num <= 10){
result = 1;
int i = 1;
while (i < num){
result = result * i;
i++;
}
System.out.println(num + "的阶乘为:" + result);
} else {
System.out.println("请输入1~10的整数!");
}
return result;
}
流程图
设计循环测试点
循环次数 | 0 次 | 1 次 | 2 次 | m 次 | n-1 次 | n 次 | n+1 次 |
---|---|---|---|---|---|---|---|
测试用例( num 的值) | 0 | 1 | 2 | 5 | 9 | 10 | 11 |
设计测试用例
测试用例编号 | 输入 | 预期输出 |
---|---|---|
testcase_01 | 0 | result=0,输出:请输入1~10的整数! |
testcase_02 | 1 | result=1,输出:1的阶乘是1 |
testcase_03 | 2 | result=2,输出:2的阶乘是2 |
testcase_04 | 5 | result=120,输出:5的阶乘是120 |
testcase_05 | 9 | result=362880,输出:9的阶乘是362880 |
testcase_06 | 10 | result=3628800,输出:10的阶乘是3628800 |
testcase_07 | 11 | result=0,输出:请输入1~10的整数! |
嵌套循环测试
测试代码
//冒泡排序
public static int[] bubble_sort(int[] numbers){
for (int i = 0; i < numbers.length - 1;i++ ){
boolean flag = false;
for (int j = 0;j < numbers.length - 1 - i;j++){
if (numbers[j] > numbers[j+1]){
int temp = 0;
temp = numbers[j];
numbers[j] = numbers[j+1];
numbers[j+1] = temp;
flag = true;
}
}
if (flag == false){
break;
}
}
return numbers;
}
流程图
设计测试用例
跳过循环:只有一个数时,内层循环不会执行,如:{3}
循环 1 次:当数组中有两个数字时,内层循环会循环一次,如:{21,2}
循环 2 次:当数组中有三个数字,且是按从小到大的顺序排列时,外层循环只会循环 1 次,为该层循环的最小值,而内层循环会循环两次,如:{1,2,21}
循环 m 次:根据简单循环测试用例设计的原则,如果循环没有最大循环次数,我们可以选择任意一个大于 2 的循环次数设计一个测试用例测试多次循环是否正确。这里我们设计一个循环 5 次的测试用例 ,通过分析代码的循环结构,我们可以知道,当传入6个数字,且数字是按从小到大的顺序排列时,外层循环只循环 1 次,内层循环会循环 5 次,如:{1,4,7,11,23,65}
2)设计外层循环的测试用例:
跳过循环:只有一个数时,外层循环不会执行,如:{3}
循环 1 次:当传入的数字都是按从小到大的顺序排列时,外层循环只会循环一次,如:{1,3,5,9}
循环 2 次:当数组中的数字需要交换一次位置时,外层循环会循环两次,如:{3,9,5,48,90}
循环 m 次:可以选择任意一个大于 2 的循环次数设计一个测试用例测试外层多次循环是否正确,如:{76,2,22,59,5,155,1,90,18}
测试用例编号 | 输入 | 预期输出 |
---|---|---|
testcase_01 | {3} | {3} |
testcase_02 | {21,2} | {2,21} |
testcase_03 | {1,2,21} | {1,2,21} |
testcase_04 | {1,4,7,11,23,65} | {1,4,7,11,23,65} |
testcase_05 | {1,3,5,9} | {1,3,5,9} |
testcase_06 | {3,9,5,48,90} | {3,5,9,48,90} |
testcase_07 | {76,2,22,59,5,155,1,90,18} | {1,2,5,18,22,76,90} |
(这里说一下可能会误会的点,首先,这里是带标记的冒泡排序,所有才会出现内存循环多次,外层循环一次的情况,第二点,由于是排序法,不存在上界,所以则不需要考虑边界n的情况)
串行循环测试
测试代码
//将数组中的最大数分解质因数
public static String test(int[] numbers){
int max_number = 0;
int factor = 2;
String result = "";
//求数据组中的最大值
for (int i = 0; i < numbers.length - 1; i++){
if (max_number < numbers[i]){
max_number = numbers[i];
}
}
//将最大值分解质因数
int tmp = max_number;
while(factor <= tmp){
if(factor == tmp){
result = result + Integer.toString(factor);
break;
} else if(tmp % factor == 0){
result = result + factor + "*";
tmp = tmp / factor;
} else{
factor++;
}
}
System.out.println(max_number + "分解质因数的结果为:" + result);
return result;
}
首先分析结构第一个循环的结果为第二个循环的判断条件,所以有关联
对第一个循环进行思考
跳过循环:当传入的数组为空时会跳过 for 循环,即:{}
循环 1 次:当传入的数组中只有一个数字时,for 循环只会循环一次,如:{6}
循环 2 次:当传入的数组中有两个数字时 for 循环会循环两次,如:{75,11}
循环 m 次:因为这个循环没有最大循环次数,所以可以选择任意一个大于 2 的循环次数设计一个测试用例测试多次循环是否正确。这里我们设计一个循环 6 次的测试用例 ,如:{20,6,90,21,45,76}
对第二个循环进行思考
跳过循环:当传入的数组为空或传入的数组中只有数字 1 时,会跳过 while 循环,因为测试 for 循环的时候我们已经设计了数组为空的测试用例,所以这里我们选择数组中只有数字 1 的用例,即:{1}
循环 1 次:当数组中的最大值为 2 时,while 循环只会执行 1 次,如:{2,1}
循环 2 次:当数组中的最大值为 3 或 4 时,while 循环会执行 2 次,如:{4,1,3,2}
循环 m 次:可以选择任意一个大于 2 的循环次数设计一个测试用例测试 while 循环是否正确,如:{27,5,50,2,100,11,21}
测试用例编号 | 输入 | 预期输出 |
---|---|---|
testcase_01 | {} | 空 |
testcase_02 | {6} | 2 * 3 |
testcase_03 | {75,11} | 3 * 5 * 5 |
testcase_04 | {20,6,90,21,45,76} | 2 * 3 * 3 * 5 |
testcase_05 | {1} | 空 |
testcase_06 | {2,1} | 2 |
testcase_07 | {4,1,3,2} | 2 * 2 |
testcase_08 | {27,5,50,2,100,11,21} | 2 * 2 * 5 * 5 |
我所了解的单元测试思路就这些了,说来还是蓝桥杯让我扩展了循环测试的方法,拜拜,下次见Ciallo~(∠・ω< )⌒★