目录
一、什么是白盒测试:
盒子内部的实现,也就是内部的代码逻辑的测试。可以依据代码内部逻辑,进行设计测试用例,对代码内部所有的路径、判断、条件进行测试(主要基于服务端的Controller层、service层、RPC层等进行正常、异常逻辑处理的测试);所以白盒测试是一种基于代码逻辑的测试,主要是根据内部的代码实现,来设计测试用例,得出预期的测试数据。代码逻辑覆盖的越高(覆盖率越高),也就是测试的越全面。
那么如何知道自己所测的逻辑被覆盖到了,有两种方式,第一种梳理代码逻辑,通过查看服务端代码日志的调用链路,结合代码自己人为去梳理;第二种,流水线中集成代码覆盖率统计工具,来统计测试代码的执行情况。
二、为什么要依赖jacoco做代码覆盖率的统计?
jacoco是一个免费的、开源的Java代码覆盖率统计工具,其原理就是通过jacoco提供的代码agent(探针),对所测代码进行插桩,以达到统计代码执行的埋点信息;
测试同学可以通过分析未覆盖到的的代码,反推测试用例设计是否充分,没有覆盖到的代码是否存在问题;没有覆盖到的代码其实是比较危险的,你现在没有覆盖到,上线后不代表用户不会走到这块逻辑;如果看不懂代码也没关系,不具备白盒测试能力,可以通过颜色来进行区分;如下图:
绿色标识:所有的分支均已覆盖;
黄色标识:部分分支被执行;
红色表示:所有的分支均未被覆盖;
三、白盒测试用例常见的几种设计方法:
1、静态方法(不去执行代码):
(1)代码评审———CodeReview
(2)静态代码扫描工具————sonar
2、动态方法(执行代码逻辑——测试同学常用):
(1)分支覆盖法:
设计测试用例,每个分支(判定)的两种情况(True / False),都得至少覆盖一次;例如下面这块代码中有2个if判断,判断执行结果有4个;
1 int rs = 0;
2 //前端传过来的值封装在JavaBean中,可以理解为 a >0 && b >0;
3 if (white.getA() > 0 && white.getB() > 0) {
4 rs = white.getA() + white.getB() + 10;
5 System.out.println("第一条语句,执行完成。。。。。。。");
6 } else {
7 rs = white.getA() + white.getB() - 10;
8 System.out.println("第二条语句,执行完成。。。。。。。");
9 }
10
11 if (white.getC() < 0) {
12 rs = 0;
13 System.out.println("第三条语句,执行完成。。。。。。。");
14 }
15
16 System.out.println("第四条语句,执行完成。。。。。。。");
17 return rs;
测试用例 | 客户端,预期 | 服务端,预期 |
a =1,b=2,c= 1 | rs :13 | 执行1,4语句 |
a=-1,b=0,c=-1 | rs :0 | 执行2、3、4语句 |
方式一:通过日志,查看实际测试结果
测试用例 | 客户端,实际 | 服务端,实际 |
a =1,b=2,c= 1 | ![]() | |
a=-1,b=0,c=-1 | |
方式二:通过代码覆盖率工具统计查看:
分支覆盖的缺点:上述代码虽然是100%的覆盖率,但是如果将 “&&” 改成 “||” ,使用以上的测试用例依然能跟上述的预期结果保持一致,所以分支覆盖是无法发现条件内部的逻辑判断的;
3 if (white.getA() > 0 || white.getB() > 0) {
(2)条件覆盖法:
设计测试用例,使得每个判断中的,每个条件都至少有一次取的True值,有一次取False值。
上述代码利用条件覆盖法,设计测试用例:
测试用例 | 客户端,预期 | 服务端,预期 |
a =12,b=0,c= 10 | rs :2 | 执行2,4语句 |
a=-12,b=15,c=-10 | rs :0 | 执行2、3、4语句 |
实际测试结果:
测试用例 | 客户端,预期 | 服务端,预期 |
a =12,b=0,c= 10 | ![]() | |
a=-12,b=15,c=-10 | ![]() |
方式二:通过代码覆盖率工具统计查看:
条件覆盖的缺点:通过代码覆盖率以及服务端日志发现,语句1那个分支是漏测的。所以说当满足100%条件覆盖的同时,并不能保证所有的分支都会被覆盖到;此时我们依赖jacoco做代码覆盖率统计的作用就体现出来了。
(3)分支条件覆盖法:
设计测试用例,每个分支(判定)的两种情况(True / False),都得至少覆盖一次;同时使得每个判断中的,每个条件都至少有一次取的True值,有一次取False值。即同时满足100%的分支覆盖,100%的条件覆盖。基本算是最优的测试方法。
上述代码利用分支、条件覆盖法,设计测试用例:
测试用例 | 客户端,预期 | 服务端,预期 |
a =22,b=44,c= 10 | rs :76 | 执行1,4语句 |
a=-22,b= 0, c=-10 | rs :0 | 执行2、3、4语句 |
实际测试结果:
测试用例 | 客户端,预期 | 服务端,预期 |
a =22,b=44,c= 10 | ![]() | |
a=-22,b= 0, c=-10 | ![]() |
(4)逻辑(路径)覆盖法:
设计测试用例,覆盖代码中所有可能组合的路径。目前测试中最常用的一种测试方法。
/* 需求:
5-10为旺季,头等舱9折,经济舱8.5折;
11月到来年4月淡季,头等舱7折,经济舱6.5折优惠。
计算出当前用户的优惠价。
*/
public double calcute(Compute compute) {
double price = 0;
//1、判断月份
if (compute.getMonth() >= 5 && compute.getMonth() <= 10) { //旺季
System.out.println("------语句1------");
//2、判断仓位类型
switch (compute.getType()) {
case "头等舱":
price = compute.getPrice() * 0.9;
System.out.println(compute.getMonth()+ "月:" + compute.getType()+ "的仓位价格为:"+ price+"元");
System.out.println("------语句2------");
break;
case "经济舱":
price = compute.getPrice() * 0.85;
System.out.println(compute.getMonth()+ "月:" + compute.getType()+ "的仓位价格为:"+ price+"元");
System.out.println("------语句3------");
break;
}
} else { //淡季
System.out.println("------语句1.1------");
switch (compute.getType()) {
case "头等舱":
price = compute.getPrice() * 0.7;
System.out.println(compute.getMonth()+ "月:" + compute.getType()+ "的仓位价格为:"+ price+"元");
System.out.println("------语句2.2------");
break;
case "经济舱":
price = compute.getPrice() * 0.65;
System.out.println(compute.getMonth()+ "月:" + compute.getType()+ "的仓位价格为:"+ price+"元");
System.out.println("------语句3.3------");
break;
}
}
System.out.println("=======语句4=======");
return price;
}
需要覆盖的测试路径:
路径1:语句1———》语句2———》语句4;
路径2:语句1———》语句3———》语句4;
路径3:语句1.1———》语句2.2———》语句4;
路径4:语句1.1———》语句3.3———》语句4;
设计测试用例数据:
测试数据 | 客户端,预期 | 服务端,预期 |
"price":1000,"month":5,"type":"头等舱" | price:900 | 执行1,2,4语句 |
"price":1000,"month":10,"type":"经济舱" | price:850 | 执行1、3、4语句 |
"price":1000,"month":4,"type":"头等舱" | price:700 | 执行1.1,2.2,4语句 |
"price":1000,"month":11,"type":"经济舱" | price:650 | 执行1.1,3.3,4语句 |
方式一:通过日志,查看实际测试结果
测试数据 | 客户端,实际结果 | 服务端,实际结果 |
"price":1000,"month":5,"type":"头等舱" | ![]() | |
"price":1000,"month":10,"type":"经济舱" | ![]() | |
"price":1000,"month":4,"type":"头等舱" | ![]() | |
"price":1000,"month":11,"type":"经济舱" | |
方式二:通过代码覆盖率工具统计查看:
四、白盒测试的优、缺点:
那么它有哪些优点呢:代码覆盖率高;可以清晰的看到那些场景被覆盖,那些未被覆盖,及时补充测试用例。
同时也有缺点:
(1)、难度大,往往业务逻辑复杂的会有很多判断,例如 if....else if... else ;for 循环;while循环;switch判断; try ... catch...等等;
(2)、业务功能可能会覆盖不全,因为我们是基于代码逻辑实现测试,如果说业务场景没有考虑全面的话,就算测的再详细,覆盖率再高,也会漏测,也达不到用户的需求 ;
因此,白盒测试也得和黑盒测试结合起来测试,对于重点的,业务逻辑复杂的,我们的进行白盒测试,保证这些模块所有的路径都被覆盖全。
五、使用jacoco框架的相关命令:
//1、运行服务时使用javaagent设置代理进行插桩
java -javaagent:jacocoagent.jar=includes=*,output=tcpserver,port=6300,address=localhost,append=true -jar springbootdemo-0.0.1-SNAPSHOT.jar
//2、依赖jacococli包dump生成在exec文件,改文件用于存储数据
java -jar jacococli.jar dump --address localhost --port 6300 --destfile ./jacoco-demo.exec
/*3、全量覆盖率统计,通过report命令,生成报告,根据文件生成覆盖率报表,但是得找到源代码,class文件路径、java文件;*/
java -jar jacococli.jar report jacoco-demo.exec --classfiles D:\springbootdemo\target\classes --sourcefiles D:\springbootdemo\src\main\java --html html-report --xml report.xml --encoding=utf-8