第1关:逻辑覆盖
任务描述
本关任务:设计测试用例,填写代码,完成白盒测试。
相关知识
为了完成本关任务,你需要掌握:
1.语句覆盖;
2.判定覆盖;
3.条件覆盖;
4.判定/条件覆盖;
5.条件组合覆盖;
6.路径覆盖。
什么是逻辑覆盖
逻辑覆盖是非常典型的一种测试方案设计方法,属于白盒测试的一种。所谓逻辑覆盖是对一系列测试过程的总称,这组测试过程逐渐进行越来越完善的通路测试。
测试数据执行(或者叫覆盖)程序逻辑的程序可以划分成不同的等级,等级从低到高分别为:语句覆盖、判定覆盖、条件覆盖、判定/条件覆盖、条件组合覆盖、路径覆盖。
语句覆盖
为了暴露程序中的错误,每个语句至少应该执行一次。语句覆盖的含义是,选择足够多的测试数据,使被测程序中的每个语句至少执行一次。
假设需要测试这样的代码:
int foo(int a, int b)
{
return a / b;
}
假如测试人员编写如下测试用例:
TeseCase: a = 10, b = 5, result=2
很明显,这样的测试用例是满足语句覆盖的,因为foo函数中的代码至少执行了一次。但是这样的测试用例找不出b不能是0的bug。
从这个例子不难看出,语句覆盖无法发现程序中逻辑运算的错误。
判定覆盖
顾名思义,判定覆盖是设计足够多的测试用例,使得程序中的每一个判断至少获得一次“真”和一次“假”,即使程序流程图中的每一个真假分支至少被执行一次。
假设需要使用判定覆盖来测试这样的代码:
int a, b;
if(a || b)
result = a + b;
else
result = a * b;
则测试用例可以如下所示:
TeseCase1: a = 1 , b = 0, result=1
TeseCase2: a = 0 , b = 0, result=0
条件覆盖
条件覆盖是指选择足够的测试用例,使得运行这些测试用例时,判定中每个条件的所有可能结果至少出现一次。
还是以刚刚的代码为例,由于a和b在if判断中的作用是表示真假,所以a和b在判断分支中都分别有两种结果,即true和false。因此只需要两个测试用例,就能满足条件覆盖。测试用例如下:
TeseCase1: a = 1 , b = 0, result=1
TeseCase2: a = 0 , b = 1, result=1
当然,测试用例也可以是这样的。
TeseCase1: a = 1 , b = 1, result=1
TeseCase2: a = 0 , b = 0, result=0
你会发现,即使满足条件覆盖,也不一定能够覆盖全部判断分支。例如例子中第一种测试用例就没有覆盖到else。
判定/条件覆盖
从覆盖的名字上看就应该能猜到,判定/条件覆盖的意思是即满足判定覆盖又满足条件覆盖。也就是说,上面例子中的第二种测试用例满足判定/条件覆盖。
条件组合覆盖
选择足够的测试用例,使得每个判定中条件的各种可能组合都至少出现一次。显然,满足“条件组合覆盖”的测试用例是一定满足“判定覆盖”、“条件覆盖”和“判定/条件覆盖”的。
由于程序中有两个判定(if else),每个判定中的每个条件都有两种结果,所以测试用例应该是这样的。
TeseCase1: a = 1 , b = 1, result=1
TeseCase2: a = 0 , b = 0, result=0
TeseCase1: a = 1 , b = 0, result=1
TeseCase2: a = 0 , b = 1, result=1
路径覆盖
路径覆盖是选取足够多的的测试数据,使程序的每条可能路径都至少执行一次。
例子中的程序有总共有两条路径,即if和else。所以测试用例可以是:
TeseCase1: a = 1 , b = 1, result=1
TeseCase2: a = 0 , b = 0, result=0
编程要求
设计满足路径覆盖的多个测试用例,用于对IsNarcissisticNumber函数进行白盒测试。在右侧编辑器 Begin-End 区间补充代码,完成白盒测试。
IsNarcissisticNumber函数是用来判断一个三位数是否为水仙花数,若是水仙花数则会返回1,否则返回0。函数代码如下:
//判断一个数是否为水仙花数,若是水仙花数则返回1,否则返回0
int IsNarcissisticNumber(int number)
{
if(number <= 99 || number >= 1000)
{
return 0;
}
int a = number%10;
int b = number/10%10;
int c = number/100%10;
if(number == a*a*a+b*b*b+c*c*c)
return 1;
return 0;
}
测试说明
平台会对你编写的代码进行测试。
测试输入:
预期输出:
总共3条路径,已覆盖3条路径
开始你的任务吧,祝你成功!
#include"func.h"
#include<stdio.h>
//输入测试用例进行测试,其中number表示测试输入,result表示预期输出
//该函数内部会调用IsNarcissisticNumber函数,若实际输出与预期输出不一致,会给予提示
void RunTestCase(int number, int result);
void WhiteTest()
{
/*请按要求设计测试用例,并调用RunTestCase*/
/*********Begin********/
RunTestCase(1000, 0);
RunTestCase(153, 1);
RunTestCase(100, 0);
/*********End*********/
}
第2关:基本路径测试
任务描述
本关任务:填写缺失代码,完成白盒测试。
相关知识
为了完成本关任务,你需要掌握基本路径测试。
基本路径测试
除了逻辑覆盖,还有一种常用的白盒测试的测试方法:基本路径测试。基本路径测试是 Tom McCabe提出的一种白盒测试技术。使用这种技术设计测试用例时,首先需要计算程序的环形复杂度,并用该复杂度为指南定义执行路径的基本集合,从该基本集合导出的测试用例可以保证程序中的每条语句至少执行一次,而且每个条件在执行时都将分别取真、假两种值。
基本路径测试的步骤为:
根据程序的执行过程画出流图
计算流图的环形复杂度
确定独立路径
设计测试用例
画流图
流图其实就是程序的流程图的简化版。其中只用圆圈来表示程序中的代码语句。下图是各种结构的示意图,流图中的箭头称为边或连接,代表控制流。任何过程设计都要被翻译成控制流图。其中,除了顺序结构,其他都为判定结构。
如果判断中的条件表达式是由一个或多个逻辑运算符 (OR, AND, NOR)连接的复合条件表达式,则需要改为一系列只有单条件的嵌套的判断。
说了这么多,不如通过例子来画出程序的流图。假设程序是这样的:
void func(int recordnum, int type)
{
int x = 0;
int y = 0;
while(recordnum-->0)
{
if(type == 0)
{
x=y+2;
break;
}
else
{
if(type == 1)
{
x=y+10;
}
else
{
x=y+20;
}
}
}
}
代码中有一个while循环结构,两个if结构,在流图中能体现出这三个结构的话,流图就算画出来了。流图如下:
其中,红线表示while循环结构,黄线表示第一个if结构,蓝线表示第二个if结构:
计算环形复杂度
环形复杂度是一种为程序逻辑复杂性提供定量测度的软件度量,将该度量用于计算程序的基本的独立路径数目,为确保所有语句至少执行一次测试数量的上界。独立路径必须包含一条在定义之前不曾用到的边。
有以下两种方法计算环形复杂度:
给定流图G的环形复杂度V(G),定义为V(G)=E-N+2,E是流图中边的数量,N是流图中结点的数量;
给定流图G的圈复杂度V(G),定义为V(G)=P+1,P是流图G中判定结点的数量。
上面的流图的环形复杂度,可以分别使用这两种方法进行计算:
方法一:环形复杂度= 10 条边 - 8 个节点 + 2 ,即环形复杂度为 4;
方法二:环形复杂度= 3 个判定节点 + 1,即环形复杂度为 4。
确定独立路径
所谓独立路径就是从流图开始的地方到结束的地方的通路。独立路径的数量等于流图的环形复杂度。上面例子的环形复杂度为4,所以独立路径总共有4条。其独立路径已在下图中分别用橙色箭头表示。
设计测试用例
确定了独立路径后,就需要设计多组测试用例,覆盖所有的独立路径。因此测试用例可以是:
TestCase1: recordnum=0, 预期x=0
TestCase2: recordnum=1, type=0, 预期x=2
TestCase3: recordnum=1, type=1, 预期x=10
TestCase4: recordnum=1, type=2, 预期x=20
编程要求
设计满足基本路径测试的多个测试用例,用于对IsPrime函数进行白盒测试。在右侧编辑器 Begin-End 区间补充代码,完成白盒测试。
IsPrime函数是用来判断一个是否为素数,若是素数则会返回1,否则返回0。函数代码如下:
//判断一个数是否为素数,若是素数则返回1,否则返回0
int IsPrime(int number)
{
if(number <= 1)
{
return 0;
}
int i;
for(i=2; i < number; ++i)
{
if(number%i==0)
return 0;
}
return 1;
}
测试说明
平台会对你编写的代码进行测试。
测试输入:
预期输出:
总共4条路径,已覆盖4条路径
开始你的任务吧,祝你成功!
#include"func.h"
#include<stdio.h>
//输入测试用例进行测试,其中number表示测试输入,result表示预期输出
//该函数内部会调用IsPrime函数,若实际输出与预期输出不一致,会给予提示
void RunTestCase(int number, int result);
void WhiteTest()
{
/*请按要求设计测试用例,并调用RunTestCase*/
/*********Begin********/
RunTestCase(0, 0);
RunTestCase(1, 0);
RunTestCase(2, 1);
RunTestCase(3, 1);
RunTestCase(13, 1);
RunTestCase(4, 0);
RunTestCase(9, 0);
/*********End*********/
}