乐趣区

关于java:Spock单元测试框架实战指南三If-esle-多分支场景测试

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

退出移动版