Wednesday Feb 23, 2011

Code coverage using Cobertura

We were requested to investigate and implement code coverage measurement and report generation to our project - Jersey. Nice opportunity to get into new thing and have a motivation to finish (since this was a "must do" type of request..).

Alright, let's get into it. At the begging, we had to decide which tool we are going to use - Cobertura [1] won, one of main arguments are nicer reports, active development and price (it's free).

There is a nice set of scripts, which can instrument classes/jars, collect & merge generated files and finally create report. This can be used in environment, where you have total control of your classpath and can easily run tests against it. Well, Jersey uses maven, so this is little more difficult, but cobertura does have maven support, so lets try it..

Maven support works perfectly when you have tests in each module and nowhere else, otherwise it is probable you'll run into similar issues as I did.. To be more descriptive: let's say, I have module A (which has some tests) and module B, which depends on A (and also has some tests). By default, code coverage results from B will contain only classes from B, which is not correct, because we want to have complete results. I hit this in Jersey, for example substitute A with "jersey-core" and B with "jersey-tests" and you are there. Solution is not that simple, because you have to somehow force B to use instrumented version of A.. which is not very straightforward using maven..

Solution:

You might find or think of few solutions, I like the one described here [2].

Main pom (profile which enables building&local deploying cobertura classified artifacts: 

        <profile>
            <id>cobertura</id>
            <activation>
                <property>
                    <name>cobertura</name>
                </property>
            </activation>
            <dependencies>
                <dependency>
                    <groupId>net.sourceforge.cobertura</groupId>
                    <artifactId>cobertura</artifactId>
                    <optional>true</optional>
                    <version>1.9.4.1</version>
                </dependency>
            </dependencies>
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.codehaus.mojo</groupId>
                        <artifactId>cobertura-maven-plugin</artifactId>
                        <executions>
                            <execution>
                                <id>cobertura-instrument</id>
                                <phase>pre-integration-test</phase>
                                <goals>
                                    <goal>instrument</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>

                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-jar-plugin</artifactId>
                        <executions>
                            <execution>
                                <id>cobertura-jar</id>
                                <phase>post-integration-test</phase>
                                <goals>
                                    <goal>jar</goal>
                                </goals>
                                <configuration>
                                    <classifier>cobertura</classifier>
                                    <classesDirectory>${basedir}/target/generated-classes/cobertura</classesDirectory>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>

                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-install-plugin</artifactId>
                        <executions>
                            <execution>
                                <id>cobertura-install</id>
                                <phase>install</phase>
                                <goals>
                                    <goal>install</goal>
                                </goals>
                                <configuration>
                                    <classifier>cobertura</classifier>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>

and additionally, you need to make sure that instrumented artifacts are used during cobertura:cobertura run. What we have here is two profiles, one activated by -Dcobertura and other one by default:

        <profile>
            <id>cobertura</id>
            <activation>
                <property>
                    <name>cobertura</name>
                </property>
            </activation>
            <dependencies>
                <dependency>
                    <groupId>com.sun.jersey</groupId>
                    <artifactId>jersey-core</artifactId>
                    <version>${project.version}</version>
                    <classifier>cobertura</classifier>
                </dependency>
            </dependencies>
        </profile>
        <profile>
            <id>default</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <dependencies>
                <dependency>
                    <groupId>com.sun.jersey</groupId>
                    <artifactId>jersey-core</artifactId>
                    <version>${project.version}</version>
                </dependency>
            </dependencies>
        </profile>

(this is taken from jersey-server module). I encountered some issues with report generation and actually jersey project structure is not simple, so I couldn't use automatic html report generation in pom file.. So we need to collect all generated cobertura.ser files separately, merge it, prepare all sources and generate report. This can be done by following commands:

# prepare sources
mkdir ../sources
find . | grep src/main/java$ | while read X ; do cp -r "$X"/\* ../sources/ ; done

# compile and run tests
mvn clean install -Dmaven.test.skip=true -Dcobertura
mvn cobertura:cobertura -Dcobertura -DforkMode=never

# collect cobertura.ser files, merge and generate report
find . | grep cobertura.ser$ | while read X  ; do cp "$X" ../cobertura$I.ser ; I=$(($I+1)) ;  done;
cobertura-merge.sh --datafile ./cobertura_final.ser ../cobertura\*
cobertura-report.sh --datafile ./cobertura_filtered.ser --destination ../report --format html ../sources/

Hope it helps somebody who wants to do code coverage for different project. Btw, coverage report for jersey: http://anise.cz/~paja/jersey/report/

[1] http://cobertura.sourceforge.net/
[2] http://foobar.lacoctelera.net/post/2008/09/15/uso-del-plugin-cobertura-dentro-un-proyecto-multi-modulo-en

About

Pavel Bucek

Search

Categories
Archives
« July 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  
       
Today