一. Expect + Where
如果业务比较复杂,对应的代码实现会有不同的分支逻辑,相似上面的伪代码:
if () {if () {// 代码逻辑} else {// 代码逻辑}
} else if () {for () {if () {// 代码逻辑} else {
// 代码逻辑
return result;
}
}
}
这样的 if else
嵌套代码因为业务的起因很难防止,如果要测试这样的代码,保障 笼罩到每一个分支逻辑 的话,应用传统的 Junit 单元测试代码写起来会很苦楚和繁琐,尽管能够应用 Junit 的 @parametered
参数化注解或者 dataprovider 的形式,但还是不够直观,调试起来也不不便
上面就联合具体业务代码解说 Spock 如何解决这种问题,还是先看下业务代码逻辑:
/**
* 身份证号码工具类 <p>
* 15 位:6 位地址码 + 6 位出生年月日(900101 代表 1990 年 1 月 1 日出世)+ 3 位程序码
* 18 位:6 位地址码 + 8 位出生年月日(19900101 代表 1990 年 1 月 1 日出世)+ 3 位程序码 + 1 位校验码
* 程序码奇数分给男性,偶数分给女性。* @author 公众号:Java 老 K
* 集体博客:www.javakk.com
*/
public class IDNumberUtils {
/**
* 通过身份证号码获取出生日期、性别、年龄
* @param certificateNo
* @return 返回的出生日期格局:1990-01-01 性别格局:F- 女,M- 男
*/
public static Map<String, String> getBirAgeSex(String certificateNo) {
String birthday = "";
String age = "";
String sex = "";
int year = Calendar.getInstance().get(Calendar.YEAR);
char[] number = certificateNo.toCharArray();
boolean flag = true;
if (number.length == 15) {for (int x = 0; x < number.length; x++) {if (!flag) return new HashMap<>();
flag = Character.isDigit(number[x]);
}
} else if (number.length == 18) {for (int x = 0; x < number.length - 1; x++) {if (!flag) return new HashMap<>();
flag = Character.isDigit(number[x]);
}
}
if (flag && certificateNo.length() == 15) {birthday = "19" + certificateNo.substring(6, 8) + "-"
+ certificateNo.substring(8, 10) + "-"
+ certificateNo.substring(10, 12);
sex = Integer.parseInt(certificateNo.substring(certificateNo.length() - 3,
certificateNo.length())) % 2 == 0 ? "女" : "男";
age = (year - Integer.parseInt("19" + certificateNo.substring(6, 8))) + "";
} else if (flag && certificateNo.length() == 18) {birthday = certificateNo.substring(6, 10) + "-"
+ certificateNo.substring(10, 12) + "-"
+ certificateNo.substring(12, 14);
sex = Integer.parseInt(certificateNo.substring(certificateNo.length() - 4,
certificateNo.length() - 1)) % 2 == 0 ? "女" : "男";
age = (year - Integer.parseInt(certificateNo.substring(6, 10))) + "";
}
Map<String, String> map = new HashMap<>();
map.put("birthday", birthday);
map.put("age", age);
map.put("sex", sex);
return map;
}
}
依据输出的身份证号码辨认出生日期、性别、年龄等信息,逻辑不简单,就是分支多,咱们来看下 Spock 代码是如何测试这种状况:
class IDNumberUtilsTest extends Specification {
@Unroll
def "身份证号:#idNo 的生日, 性别, 年龄是:#result"() {
expect: "when + then 组合"
IDNumberUtils.getBirAgeSex(idNo) == result
where: "表格形式测试不同的分支逻辑"
idNo || result
"310168199809187333" || ["birthday": "1998-09-18", "sex": "男", "age": "22"]
"320168200212084268" || ["birthday": "2002-12-08", "sex": "女", "age": "18"]
"330168199301214267" || ["birthday": "1993-01-21", "sex": "女", "age": "27"]
"411281870628201" || ["birthday": "1987-06-28", "sex": "男", "age": "33"]
"427281730307862" || ["birthday": "1973-03-07", "sex": "女", "age": "47"]
"479281691111377" || ["birthday": "1969-11-11", "sex": "男", "age": "51"]
}
}
在测试方法体的第一行应用了 expect
标签,它的作用是 when
+ then
标签的组合,即 “ 什么时候做什么 + 而后验证什么后果 ” 组合起来
即当调用IDNumberUtils.getBirAgeSex(idNo)
办法时,验证后果是 result,result 如何验证对应的就是 where 里的 result 一列的数据,当输出参数 idNo 是 ”310168199809187333″ 时,返回后果是: [“birthday”: “1998-09-18”, “sex”: “ 男 ”, “age”: “22”]
expect
能够独自应用,能够不须要where
,只是在这个场景须要
@Unroll
注解示意开展 where 标签上面的每一行测试,作为独自的 case 跑,再加上办法体 ” 身份证号:#idNo 的生日, 性别, 年龄是:#result”,应用了 groovy 的字面量个性,动静替换字符串变量,这样每次跑的单测后果展现也很容易辨别,不便了解,如下:
每个测试后果对应 where 标签里的一行
另外在 intellij idea 里能够 run with coverage 的运行形式查看单测覆盖率状况:
右边圈出的绿色柱子示意单测已笼罩的代码,红色柱子是单测还没有笼罩到的分支,如果须要进一步提高覆盖率,只需在 where 表格中再增加一行测试条件即可
(残缺的源码在公众号: java 老 k 里回复 spock 获取)
二. Jacoco
Jacoco 是统计单元测试覆盖率的一种工具,当然 Spock 也自带了覆盖率统计的性能,这里应用第三方 Jacoco 的起因次要是国内公司应用的比拟多一些,包含咱们公司当初应用的也是 Jacoco,所以为了兼容就以 Jacoco 来查看单测覆盖率
当然你也能够应用 Spock 自带的单测覆盖率工具,在前面的文章里会介绍具体如何配置,本篇次要说下如何通过 Jacoco 确认分支是否齐全笼罩到
在 pom 文件里援用 jacoco 的插件: jacoco-maven-plugin
, 而后执行mvn test
命令,胜利后会在 target 目录下生成单元测试覆盖率的报告:
(具体生成门路能够设置)
应用浏览器关上 index.html,就能看到所有的单测覆盖率统计指标:
点击包名找到咱们方才测试的 IDNumberUtils 类,关上后能够看到具体的笼罩状况:
绿色背景示意齐全笼罩,黄色是局部笼罩,红色没有笼罩到
比方第 45 行黄色背景的 else if()
判断,提醒有 4 分之 2 的分支缺失,尽管它上面的代码也被笼罩了 (显示为绿色),然而因为咱们的单测代码没有测试 flag 为 false,以及certificateNo.length()!=18
的场景,所以只能算笼罩了一半(2/4)
讲这个的起因是因为如果公司设置的 分支覆盖率 要求大于 50%,那么你就要在单元测试代码里额定减少这种状况的测试,即便业务代码里没有这样例外情况的解决
这种状况跟具体应用哪种单测框架没关系,因为这只是分支覆盖率统计的规定,只不过应用 Spock 的话,解决起来会更简略,只需在 where 下减少一行针对的测试数据即可
文章起源:http://javakk.com/281.html