前言
随着敏捷开发的流行,编写单元测试已经成为业界共识。但如何来衡量单元测试的质量呢?有些管理者片面追求单元测试的数量,导致底下的开发人员投机取巧,编写出大量的重复测试,数量上去了,质量却依然原地踏步。相比单纯追求单元测试的数量,分析单元测试的代码覆盖率是一种更为可行的方式。JaCoCo(Java Code Coverage)就是一种分析单元测试覆盖率的工具,使用它运行单元测试后,可以给出代码中哪些部分被单元测试测到,哪些部分没有没测到,并且给出整个项目的单元测试覆盖情况百分比,看上去一目了然。EclEmma 是基于 JaCoCo 的一个 Eclipse 插件,开发人员可以方便的和其交互。因此,本文先从 EclEmma 入手,给读者一个直观的体验。
使用 EclEmma 在 Eclipse 中查看单元测试覆盖率
EclEmma 是基于 JaCoCo 的 Eclipse 插件,使用它,开发人员可以直观地看到单元测试的覆盖情况。
安装 EclEmma
打开 Eclipse 的软件市场,在其中搜索 EclEmma,找到后完成安装,如下图所示:
图 1. 安装 EclEmma
安装完成后,Eclipse 的工具条里会多出下面这样一个图标:
图 2. Coverage 图标
分析单元测试覆盖率
成功安装 EclEmma 后,就可以试着用它来分析项目的单元测试覆盖率了。为了方便演示,我们使用 Eclipse 创建了一个标准 Java 工程。其中包含一个数学工具类,用来计算三个数中的最大值,代码如下:
清单 1. 数学工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package com.dw.math;
public class MathUtil {
public static int max(int a, int b, int c){
if(a > b){
if(a > c){
return a;
}else{
return c;
}
}else{
if(b > c){
return b;
}else{
return c;
}
}
}
}
|
可以看到,这里的算法稍微有点复杂,使用到了多个条件判断分支,因此,特别适合为其编写单元测试。第一个版本的单元测试如下:
清单 2. 第一个版本的单元测试
1
2
3
4
5
6
7
8
9
|
package com.dw.math;
import static org.junit.Assert.*;
import org.junit.Test;
public class MathUtilTest {
@Test
public void test_max_1_2_3() {
assertEquals(3, MathUtil.max(1, 2, 3));
}
}
|
试着运行一下单元测试覆盖率分析工具:40.0%!似乎不太理想。展开分析报告,双击后在编辑器里可以看到覆盖情况被不同的颜色标识出来,其中绿颜色表示代码被单元测试覆盖到,黄色表示部分覆盖,红色则表示完全没有覆盖到,如下图所示:
图 3. 单元测试覆盖率报告
让我们尝试多加一些单元测试,来改善这种情况,请看下面第二个版本的单元测试:
清单 3. 第二个版本的单元测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package com.dw.math;
import static org.junit.Assert.*;
import org.junit.Test;
public class MathUtilTest {
@Test
public void test_max_1_2_3() {
assertEquals(3, MathUtil.max(1, 2, 3));
}
@Test
public void test_max_10_20_30() {
assertEquals(30, MathUtil.max(10, 20, 30));
}
@Test
public void test_max_100_200_300() {
assertEquals(300, MathUtil.max(100, 200, 300));
}
}
|
测试覆盖率还是 40.0%!虽然我们额外再加了两个测试,但覆盖率没有半点提升,这些单元测试其实是重复的,它们在重复测试同一段代码。如果单纯追求单元测试的数量,那么这无疑会给管理者造成错觉,他们觉得单元测试的数量增加了,软件的质量更有保证了;而对于那些喜欢偷懒的程序员,也蒙混过关,但却给软件质量埋下了隐患。让我们删掉这些重复的单元测试,重新思考一下怎么测试这个方法。
首先我们要测试正常情况,这其中又包含 3 种情况:第一个参数最大,第二个参数最大,以及最后一个参数最大。然后我们还需测试几种特殊情况,比如三个参数相同,三个参数中,其中两个相同。让我们照此思路重新编写单元测试:
清单 4. 第三个版本的单元测试
1
2
3
4
5
6
7
|