目录
满足组合覆盖的测试用例集一定满足判定覆盖、条件覆盖和判定/条件覆盖。
1.1 什么是单元测试
单元测试是V模型的测试级别最低的测试,是其他测试级别的基础。
单元测试(Unit Testing)又称模块测试,是对构成软件的最小单元进行的测试
1.2单元测试方法
单元测试的一个方法是在编写代码之前就完成测试用例的编写和测试用例自动化,这种方法被称之为测试优先的方法或测试驱动开发。这是高度迭代的方法,并且取决于如下的循环周期:测试用例的开发,构建软件单元和渐增集成,执行单元测试,修正任何问题并反复循环,直到它们全部通过测试。
在进行单元测试时,我们常用白盒测试技术设计测试用例,采用自动化测试方法(单元测试框架,例如JUnit,NUnit等)执行单元测试。
1.3 JUnit起步
1.将JUnit库文件加入Java项目构建路径。
2.@BeforeClass、@AfterClass、@Before、@After、@Test是Java的注解,所谓注解,是对类或方法的说明。它们的含义如下:
- @Before:用它注解的方法是初始化方法,在每一个测试方法执行前都要执行这个方法(注意与@BeforeClass的区别,后者在所有测试方法执行前执行一次)
- @After:用它注解的方法用于释放资源,在每一个测试方法执行后都要执行这个方法(注意与AfterClass区别,后者在所有测试方法执行后执行一次)
- @Test:用它注解的方法是测试方法,测试的核心代码写在这个方法中
- @BeforeClass:在所有测试方法执行前执行
- @AfterClass: 在所有测试方法执行后执行
3.一个JUnit4的单元测试用例执行顺序为: @BeforeClass -> @Before -> @Test -> @After -> @AfterClass;
4.测试代码编写的三部曲,创建对象(实例化被测类),调用被测类的方法,比较期望结果与实际结果。
补充JAVA(单元测试代码):
- 创建对象:类名 对象名=new 构造方法();
- 注意:构造方法名和类名相同;
- 对象名只能由数字、字母、下划线、美元符号组成,首字母不能是数字;
调用被测类的方法:
- 数据类型 变量名=对象名.方法名();
- 对象名.属性;
- 断言:assertEquals(实际结果(变量值),期望值);
1.4测试用例
测试用例,是指为了某个特定的测试目标而设计的一组测试输入、执行条件以及预期结果。测试用例的内容一般包括测试目标、测试环境、输入数据、测试步骤、预期结果、测试脚本等。
1.4.1语句覆盖
在设计测试用例时,选择足够多的测试用例,使得被测程序的每一行语句都至少执行一遍。这种设计测试用例的方法称为语句覆盖。
单元设计要求:设计一个方法,输入两个整型参数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 |
要求我们实现的是 “当x小于5或者y=5时将x和y的和作为结果返回”,但是程序实现的却是“当x小于5并且y=5时将x和y的和作为结果返回”。
语句覆盖不能发现判定中的问题
1.4.2判定覆盖
希望通过设计足够多的测试用例,使得程序中的每个判定至少都获得一次“真”值和“假”值, 也就是使程序中的每个取“真”分支和取“假”分支至少均经历一次,这种设计测试用例的方法称为判定覆盖,也称为“分支覆盖”
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 |
上面的测试用例集也满足语句覆盖的要求,事实上,如果不考虑循环语句,满足判定覆盖的测试用例集一定满足语句覆盖。由于循环语句可以一次都没有执行,因此,一般来说,满足判定覆盖的测试用例集不一定满足语句覆盖。
当x=6,y=0时,上面的程序就会出现问题。
组成判定的条件是复杂的,我们还应该对组成判定的各个条件进行测试。
1.4.3条件覆盖
通过设计足够多的测试用例,使得程序中每个判定包含的每个条件的可能取值(真/假)都至少出现一次。这种设计测试用例的方法称为条件覆盖。
序号 |
x |
y |
预期结果 |
x<5 |
y==5 |
x < 5 || y == 5 |
路径 |
1 |
2 |
1 |
3 |
T |
F |
T |
|
2 |
6 |
5 |
1 |
F |
T |
T |
1.4.3判定/条件覆盖
通过设计足够多的测试用例,使得程序中每个判定包含的每个条件的所有情况(真/假)至少出现一次,并且每个判定本身的判定结果(真/假)也至少出现一次。这种设计测试用例的方法称为判定/条件覆盖。
满足判定/条件覆盖的测试用例集一定同时满足判定覆盖和条件覆盖。
序号 |
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 |
1.4.5组合覆盖
通过设计足够多的测试用例,使得程序中每个判定的所有可能的条件取值组合都至少出现一次。这种设计测试用例的方法称为组合覆盖。
满足组合覆盖的测试用例集一定满足判定覆盖、条件覆盖和判定/条件覆盖。
序号 |
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 |
1.5基本路径覆盖
通过设计足够多的测试用例,要求覆盖程序的基本路径集合中所有可能的路径。这种设计测试用例的方法称为基本路径覆盖。
1.5.1环路复杂度
- V(G)=e-n+2
- V(G)=区域数
- V(G)=判定节点数+1
- 独立路径:至少包含一条在其他独立路径中没包含的边
- 程序流图的基本路径集合包含的路径数,称为独立路径数。
- 独立路径集合不唯一
- 独立路径数不会超过环路复杂度
1.5.2基本路径覆盖示例
//将一个正整数分解为质因数的Java程序如下
public class Number {
public static String primeNumber(int n){
int k=2;
String rs=n+"=";
while(k<=n)
{
if(k==n)
{
rs=rs+n;
break;
}else
{
if(n%k==0)
{
rs=rs+k+"*";
n=n/k;
}else
{
k++;
}
}
}
return rs;
}
}
计算环形复杂度: V(G)=9-8+2=2+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
根据基本路径设计测试用例,覆盖基本路径集合中的所有路径
将V(G)定义为:
- V(G)=e-n+2
- 这里,e是控制流图的边数,n是控制流图的节点数。
我们还可以用如下两个方法计算环形复杂度:
- V(G)=区域数
- V(G)=判定节点数+1
- 这里,区域是指由边包围起来的形状,图中没有被边包围的部分也算一个区域。判定节点是有多个边以它作为起点的节点。