Saturday, July 30, 2011

Integrating code coverage (integration-test phase) with maven

This article is about integrating code coverage with maven (test or integration-test phase). Somehow, whenever I try to mess with maven, it gives me a hard time probably because its documentation is not well and scattered.

So, after a days research I came across 3 plugins : EMMA, Cobertura and Clover.
I will not go into details of clover as it is paid and has licensing issues (you need license war's although you can get 30 day trial war). I somehow found it difficult to integrate with tomcat.

I was pretty new to understand how code coverage is computed. After doing some research I figured out that it involves 4 steps :
1) Instrumentation : All classes will be added with some extra code which help analyze line hits in code coverage later. So, plugin will generate instrumented jar and an additional file like (.em/.ec by emma or .ser file by cobertura).
2) Running test cases/Deployment on J2EE container : Running in test phase is straight forward.
To deploy in say tomcat in my case, firstly, you need to specify environment variable to where plugin will dump instrumented data for during test phase on tomcat.
3) After tomcat is deployed, junits are run. And when tomcat stops, plugin will dump the instrumented data file which will be used to generate code coverage report.
4) Merge dump files and generate report in different formates like html,xml,txt.

Now, each plugin has a different way of dumping runtime data and initial instrumentation data. Lets discuss EMMA and Cobertura in their approach (Note : Please choose maven "phase" according to your needs)

Cobertura

Before we look into cobertura, a word of caution. If your project uses spring and jdk proxies, please use EMMA instead, as in my case. Cobertura has problems with org.springframework.aop.framework.ProxyFactoryBean and jdk proxies. If you are using AOP proxies and have annotation at class level and not interface level, then you can use cobertura.

1) To instrument classes use cobertura-maven-plugin .
Here is a snippet to instrument jars


 
  cobertura
  
   
    net.sourceforge.cobertura
    cobertura
    true
    1.9.4.1
   
  
  
   
    
     org.codehaus.mojo
     cobertura-maven-plugin
     
      
       cobertura-instrument
       process-classes
       
        instrument
       
      
     
    
   
  
 


This will generate a .ser file which will contain meta data about each class. It is important that before deploying, .ser should contain meta data about all the classes you are interested in finding. Say if you have 2 different jar's instrument in 2 different projects, use cobertura-merge before deploying on tomcat (if you are interested in code coverage of jar 1 in project 2..say common library in my case). This can be done using ant task (do only if you need to merge 2 different metadata ser before hand. In single project it is not needed). You can choose appropriate phase before deployment. In my project, I used it at jar verify stage.


 maven-antrun-plugin
 1.6
 
  
   merge-ser-pre
   verify
   
    
     < taskdef classpathref="maven.runtime.classpath"
     resource="tasks.properties" />
     < taskdef classpathref="maven.runtime.classpath"
     resource="net/sf/antcontrib/antcontrib.properties" />
     < echo message="Executing cobertura merge" />
     
      
       < include name="cobertura.ser" />

      
       < include name="cobertura.ser" />

     
    
   
   
    run
   
  
 
 
  
   ant-contrib
   ant-contrib
   20020829
  
 


Now, to deploy on tomcat you need following dependency in war packaging

 
  net.sourceforge.cobertura
  cobertura
  1.9.4.1
  jar
  compile
 

I am using cargo plugin to start tomcat..so here is how to set dump file system vairable :

 ...
 
  somelocation/cobertura.ser
  
 


It is extremely important to note that this dumping .ser should be same as previously metadata instrumented .ser . After runtime, EMMA should dump information into same .ser which was instrumented initially during jar creation or merged ser in case of multi projects or else code coverage will be 100%.

Finally, we need to create a report from this .ser . If you have different .ser from different projects computed separately, you can again use cobertura merge to merge them into a final ser.

Cobertura reports are better as html reports looks visually much better that EMMA. Apart from lines covered, cobertura report also tells how many times each line was hit. This is not covered in EMMA.


 org.apache.maven.plugins
 maven-antrun-plugin
 
  
   verify
   
    
     < taskdef classpathref="maven.runtime.classpath"
     resource="tasks.properties" />
     < taskdef
     classpathref="maven.runtime.classpath"
     resource="net/sf/antcontrib/antcontrib.properties" />
     < available
     file="./1.ser" property="ser.file.exists" />
     
      < equals arg1="${ser.file.exists}" arg2="true" />
      
       < echo message="Executing cobertura report" />
       < mkdir
       dir="${project.build.directory}/site/cobertura" />
       <
       cobertura-report format="xml" srcdir="../java/src/main/java"
       destdir="${project.build.directory}/site/cobertura"
       datafile="./final.ser" />

       
        <
        fileset dir="../commons/src/main/java" />
        < fileset
        dir="../algo/java/src/main/java" />
        < fileset
        dir="../practise/java/src/main/java" />
       
      
      
       < echo message="No SER file found."/>

     
    
   
   
    run
   
  
 
 
  
   ant-contrib
   ant-contrib
   20020829
  
 


EMMA
EMMA is relatively easier to integrate. For instrumentation initially use :


 emma
 
  
   
    org.codehaus.mojo
    emma-maven-plugin
    1.0-alpha-3
    true
    
     
      process-classes
      
       instrument
      
     
    
   
  
 


This will create .em file . EMMA does not want you to put this .em file in tomcat container unlike cobertura which uses same .ser file to dump runtime information. So, just deploy your war on tomcat with your choosen new dump file path.
you will need following dependency :


 
  emma
  emma
  2.1.5320
  jar
  compile
 


Here is code in cargo :


 
  false
  ${basedir}/target/final.ec
  
 


If you have multiple projects to be build, emma sometimes will give you "java.net.BindException:Address in use" error and not allow tomcat to start. This can either be solved by using emma.rt.control.port system variable and setting different values for different project jars. But a better approach is to somehow disable it using emma.rt.control, setting it to false(Thats why comment port)

After tomcat shuts, it will generated this final.ec

And finally we need to merge (in case of multi projects ) and create final report. Here is the code :


 
  emma
  emma
  2.1.5320
  jar
  compile
 
 
  emma
  emma_ant
  2.1.5320
  jar
  compile
 


 
  
   maven-antrun-plugin
   1.6
   
    
     report emma
     verify
     
      
       < taskdef classpathref="maven.runtime.classpath"
       resource="emma_ant.properties" />
       < sleep seconds="15" />
       
        
         
          < include name="algo/webapp/target/final.ec" />
          < include name="algo/java/target/coverage.em" />
          < include name="practise/webapp/target/final.ec" />
          < include name="practise/java/target/coverage.em" />

        
        
         
          < include name="target/final.emma" />

         < txt outfile="target/coverage.txt" />
         < html outfile="target/coverage.html" />
        
       
      
     
     
      run
     
    
   
   
    
     ant-contrib
     ant-contrib
     20020829
    
   
  
 


Congrats..check your report :)

I spent almost 4 night-outs on integration maven with cargo and integration tests. Hope you are able to do it quickly :)

No comments:

Post a Comment