一. 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