使用cobertura来测量单元测试的代码覆盖情况
Cobertura是一个基于jcoverage的免费Java工具,它能够显示哪一部分代码被你的测试所覆盖,并可生成HTML或XML报告. 本文将介绍如何在项目中使用cobertura来测量单元测试的代码覆盖情况.
首先创建一个Java项目,本文以Eclipse为例:
项目名: CodeCoverageTest
结构图如下:
接下,创建一个Ant构建配置文件,帮我们实现自动化的编译,单元测试以及cobertura来测量单元测试的代码覆盖情况。
Ant的 build.xml文件内容如下:
1
<?
xml version="1.0" encoding="UTF-8"
?>
2
3 < project name ="cobertura.examples.basic" default ="coverage" basedir ="." >
4
5 <!-- 引用 build.properties文件 配置路径信息 -->
6 < property file ="build.properties" />
7
8 <!-- 设置 cobertura路径 -->
9 < path id ="cobertura.classpath" >
10 < fileset dir ="${cobertura.dir}" >
11 < include name ="cobertura.jar" />
12 < include name ="lib/**/*.jar" />
13 </ fileset >
14 </ path >
15
16 <!-- 配置 cobatura ant 扩展任务 -->
17 < taskdef classpathref ="cobertura.classpath" resource ="tasks.properties" />
18
19 < target name ="init" >
20 < mkdir dir ="${classes.dir}" />
21 < mkdir dir ="${instrumented.dir}" />
22 < mkdir dir ="${reports.xml.dir}" />
23 < mkdir dir ="${reports.html.dir}" />
24 < mkdir dir ="${coverage.xml.dir}" />
25 < mkdir dir ="${coverage.html.dir}" />
26 </ target >
27
28 <!-- 编译源代码 -->
29 < target name ="compile" depends ="init" >
30 < javac srcdir ="${src.dir}:${test.dir}:${src.conf.dir}:${test.conf.dir}" destdir ="${classes.dir}" debug ="yes" >
31 < classpath refid ="cobertura.classpath" />
32 </ javac >
33 </ target >
34
35 < target name ="instrument" depends ="init,compile" >
36 <!--
37 Remove the coverage data file and any old instrumentation.
38 -->
39 < delete file ="cobertura.ser" />
40 < delete dir ="${instrumented.dir}" />
41
42 <!--
43 Instrument the application classes, writing the
44 instrumented classes into ${build.instrumented.dir}.
45 -->
46 < cobertura-instrument todir ="${instrumented.dir}" >
47 <!--
48 The following line causes instrument to ignore any
49 source line containing a reference to log4j, for the
50 purposes of coverage reporting.
51 -->
52 < ignore regex ="org.apache.log4j.*" />
53
54 < fileset dir ="${classes.dir}" >
55 <!--
56 Instrument all the application classes, but
57 don't instrument the test classes.
58 -->
59 < include name ="**/*.class" />
60 < exclude name ="**/*Test.class" />
61 </ fileset >
62 </ cobertura-instrument >
63 </ target >
64
65 <!-- 单元测试 -->
66 < target name ="test" depends ="init,compile" >
67 < junit fork ="yes" dir ="${basedir}" failureProperty ="test.failed" >
68 <!--
69 Note the classpath order: instrumented classes are before the
70 original (uninstrumented) classes. This is important.
71 -->
72 < classpath location ="${instrumented.dir}" />
73 < classpath location ="${classes.dir}" />
74
75 <!--
76 The instrumented classes reference classes used by the
77 Cobertura runtime, so Cobertura and its dependencies
78 must be on your classpath.
79 -->
80 < classpath refid ="cobertura.classpath" />
81
82 < formatter type ="xml" />
83 < test name ="${testcase}" todir ="${reports.xml.dir}" if ="testcase" />
84 < batchtest todir ="${reports.xml.dir}" unless ="testcase" >
85 < fileset dir ="${test.dir}" >
86 < include name ="**/*Test.java" />
87 </ fileset >
88 < fileset dir ="${src.dir}" >
89 < include name ="**/*Test.java" />
90 </ fileset >
91 </ batchtest >
92 </ junit >
93
94 < junitreport todir ="${reports.xml.dir}" >
95 < fileset dir ="${reports.xml.dir}" >
96 < include name ="TEST-*.xml" />
97 </ fileset >
98 < report format ="frames" todir ="${reports.html.dir}" />
99 </ junitreport >
100 </ target >
101
102 < target name ="coverage-check" >
103 < cobertura-check branchrate ="34" totallinerate ="100" />
104 </ target >
105
106 <!-- 生成 coverage xml格式报告 -->
107 < target name ="coverage-report" >
108 <!--
109 Generate an XML file containing the coverage data using
110 the "srcdir" attribute.
111 -->
112 < cobertura-report srcdir ="${src.dir}" destdir ="${coverage.xml.dir}" format ="xml" />
113 </ target >
114
115 <!-- 生成 coverage html格式报告 -->
116 < target name ="alternate-coverage-report" >
117 <!--
118 Generate a series of HTML files containing the coverage
119 data in a user-readable form using nested source filesets.
120 -->
121 < cobertura-report destdir ="${coverage.html.dir}" >
122 < fileset dir ="${src.dir}" >
123 < include name ="**/*.java" />
124 </ fileset >
125 </ cobertura-report >
126 </ target >
127
128 < target name ="clean" description ="Remove all files created by the build/test process." >
129 < delete dir ="${classes.dir}" />
130 < delete dir ="${instrumented.dir}" />
131 < delete dir ="${reports.dir}" />
132 < delete file ="cobertura.log" />
133 < delete file ="cobertura.ser" />
134 </ target >
135
136 < target name ="coverage" depends ="compile,instrument,test,coverage-report,alternate-coverage-report" description ="Compile, instrument ourself, run the tests and generate JUnit and coverage reports." />
137
138 </ project >
139
2
3 < project name ="cobertura.examples.basic" default ="coverage" basedir ="." >
4
5 <!-- 引用 build.properties文件 配置路径信息 -->
6 < property file ="build.properties" />
7
8 <!-- 设置 cobertura路径 -->
9 < path id ="cobertura.classpath" >
10 < fileset dir ="${cobertura.dir}" >
11 < include name ="cobertura.jar" />
12 < include name ="lib/**/*.jar" />
13 </ fileset >
14 </ path >
15
16 <!-- 配置 cobatura ant 扩展任务 -->
17 < taskdef classpathref ="cobertura.classpath" resource ="tasks.properties" />
18
19 < target name ="init" >
20 < mkdir dir ="${classes.dir}" />
21 < mkdir dir ="${instrumented.dir}" />
22 < mkdir dir ="${reports.xml.dir}" />
23 < mkdir dir ="${reports.html.dir}" />
24 < mkdir dir ="${coverage.xml.dir}" />
25 < mkdir dir ="${coverage.html.dir}" />
26 </ target >
27
28 <!-- 编译源代码 -->
29 < target name ="compile" depends ="init" >
30 < javac srcdir ="${src.dir}:${test.dir}:${src.conf.dir}:${test.conf.dir}" destdir ="${classes.dir}" debug ="yes" >
31 < classpath refid ="cobertura.classpath" />
32 </ javac >
33 </ target >
34
35 < target name ="instrument" depends ="init,compile" >
36 <!--
37 Remove the coverage data file and any old instrumentation.
38 -->
39 < delete file ="cobertura.ser" />
40 < delete dir ="${instrumented.dir}" />
41
42 <!--
43 Instrument the application classes, writing the
44 instrumented classes into ${build.instrumented.dir}.
45 -->
46 < cobertura-instrument todir ="${instrumented.dir}" >
47 <!--
48 The following line causes instrument to ignore any
49 source line containing a reference to log4j, for the
50 purposes of coverage reporting.
51 -->
52 < ignore regex ="org.apache.log4j.*" />
53
54 < fileset dir ="${classes.dir}" >
55 <!--
56 Instrument all the application classes, but
57 don't instrument the test classes.
58 -->
59 < include name ="**/*.class" />
60 < exclude name ="**/*Test.class" />
61 </ fileset >
62 </ cobertura-instrument >
63 </ target >
64
65 <!-- 单元测试 -->
66 < target name ="test" depends ="init,compile" >
67 < junit fork ="yes" dir ="${basedir}" failureProperty ="test.failed" >
68 <!--
69 Note the classpath order: instrumented classes are before the
70 original (uninstrumented) classes. This is important.
71 -->
72 < classpath location ="${instrumented.dir}" />
73 < classpath location ="${classes.dir}" />
74
75 <!--
76 The instrumented classes reference classes used by the
77 Cobertura runtime, so Cobertura and its dependencies
78 must be on your classpath.
79 -->
80 < classpath refid ="cobertura.classpath" />
81
82 < formatter type ="xml" />
83 < test name ="${testcase}" todir ="${reports.xml.dir}" if ="testcase" />
84 < batchtest todir ="${reports.xml.dir}" unless ="testcase" >
85 < fileset dir ="${test.dir}" >
86 < include name ="**/*Test.java" />
87 </ fileset >
88 < fileset dir ="${src.dir}" >
89 < include name ="**/*Test.java" />
90 </ fileset >
91 </ batchtest >
92 </ junit >
93
94 < junitreport todir ="${reports.xml.dir}" >
95 < fileset dir ="${reports.xml.dir}" >
96 < include name ="TEST-*.xml" />
97 </ fileset >
98 < report format ="frames" todir ="${reports.html.dir}" />
99 </ junitreport >
100 </ target >
101
102 < target name ="coverage-check" >
103 < cobertura-check branchrate ="34" totallinerate ="100" />
104 </ target >
105
106 <!-- 生成 coverage xml格式报告 -->
107 < target name ="coverage-report" >
108 <!--
109 Generate an XML file containing the coverage data using
110 the "srcdir" attribute.
111 -->
112 < cobertura-report srcdir ="${src.dir}" destdir ="${coverage.xml.dir}" format ="xml" />
113 </ target >
114
115 <!-- 生成 coverage html格式报告 -->
116 < target name ="alternate-coverage-report" >
117 <!--
118 Generate a series of HTML files containing the coverage
119 data in a user-readable form using nested source filesets.
120 -->
121 < cobertura-report destdir ="${coverage.html.dir}" >
122 < fileset dir ="${src.dir}" >
123 < include name ="**/*.java" />
124 </ fileset >
125 </ cobertura-report >
126 </ target >
127
128 < target name ="clean" description ="Remove all files created by the build/test process." >
129 < delete dir ="${classes.dir}" />
130 < delete dir ="${instrumented.dir}" />
131 < delete dir ="${reports.dir}" />
132 < delete file ="cobertura.log" />
133 < delete file ="cobertura.ser" />
134 </ target >
135
136 < target name ="coverage" depends ="compile,instrument,test,coverage-report,alternate-coverage-report" description ="Compile, instrument ourself, run the tests and generate JUnit and coverage reports." />
137
138 </ project >
139
build.properties文件
1
# The source code for the examples can be found in this directory
2 src.dir = ../src/java
3 src.conf.dir = ../src/conf
4 test.dir = ../test/java
5 test.conf.dir = ../src/conf
6
7 # The path to cobertura.jar
8 cobertura.dir = cobertura
9
10 # Classes generated by the javac compiler are deposited in this directory
11 classes.dir = ../bin
12
13 # Instrumented classes are deposited into this directory
14 instrumented.dir = instrumented
15
16 # All reports go into this directory
17 reports.dir = reports
18
19 # Unit test reports from JUnit are deposited into this directory
20 reports.xml.dir = ${reports.dir}/junit-xml
21 reports.html.dir = ${reports.dir}/junit-html
22
23 # Coverage reports are deposited into these directories
24 coverage.xml.dir = ${reports.dir}/cobertura-xml
25 coverage.html.dir = ${reports.dir}/cobertura-html
26
2 src.dir = ../src/java
3 src.conf.dir = ../src/conf
4 test.dir = ../test/java
5 test.conf.dir = ../src/conf
6
7 # The path to cobertura.jar
8 cobertura.dir = cobertura
9
10 # Classes generated by the javac compiler are deposited in this directory
11 classes.dir = ../bin
12
13 # Instrumented classes are deposited into this directory
14 instrumented.dir = instrumented
15
16 # All reports go into this directory
17 reports.dir = reports
18
19 # Unit test reports from JUnit are deposited into this directory
20 reports.xml.dir = ${reports.dir}/junit-xml
21 reports.html.dir = ${reports.dir}/junit-html
22
23 # Coverage reports are deposited into these directories
24 coverage.xml.dir = ${reports.dir}/cobertura-xml
25 coverage.html.dir = ${reports.dir}/cobertura-html
26
编写示例代码
Hello.java
1
package
com.xmatthew.practise.cobertura;
2
3 public class Hello {
4
5 public String birthday(String name) {
6 return " happy birthday to " + name;
7 }
8
9 public String christmas(String name) {
10 return " merry christmas " + name;
11 }
12
13 }
2
3 public class Hello {
4
5 public String birthday(String name) {
6 return " happy birthday to " + name;
7 }
8
9 public String christmas(String name) {
10 return " merry christmas " + name;
11 }
12
13 }
编写测试Hello.java的测试类,进行单元测试
HelloTest.java
1
package
com.xmatthew.practise.cobertura;
2
3 import junit.framework.Assert;
4 import junit.framework.TestCase;
5
6 public class HelloTest extends TestCase {
7
8 private Hello hello;
9
10 protected void setUp() throws Exception {
11 hello = new Hello();
12 }
13
14 protected void tearDown() throws Exception {
15 hello = null ;
16 }
17
18 public void testBirthday() {
19 String expected = " happy birthday to matt " ;
20 Assert.assertEquals(expected, hello.birthday( " matt " ));
21 }
22
23 public void testChristmas() {
24 String expected = " merry christmas matt " ;
25 Assert.assertEquals(expected, hello.christmas( " matt " ));
26 }
27
28 }
29
2
3 import junit.framework.Assert;
4 import junit.framework.TestCase;
5
6 public class HelloTest extends TestCase {
7
8 private Hello hello;
9
10 protected void setUp() throws Exception {
11 hello = new Hello();
12 }
13
14 protected void tearDown() throws Exception {
15 hello = null ;
16 }
17
18 public void testBirthday() {
19 String expected = " happy birthday to matt " ;
20 Assert.assertEquals(expected, hello.birthday( " matt " ));
21 }
22
23 public void testChristmas() {
24 String expected = " merry christmas matt " ;
25 Assert.assertEquals(expected, hello.christmas( " matt " ));
26 }
27
28 }
29
接下来,我们运行 Cobertura来进行测试
运行 Cobertura,测试在Eclipse运行时,要在Ant插件的运行类库环境中加入cobertura相关类库。
查看运行结果报告:
JUnit报告
Cobertura报告
到目前已经完成整个项目的基于Ant进行编译,测试以及生成Cobertura测试报告的开发IDE环境。这个项目框架足以应用日常的开发需求。
Cobertura官网地址: http://cobertura.sourceforge.net/
Good Luck!
Yours Matthew!