Help

Built with Seam

You can find the full source code for this website in the Seam package in the directory /examples/wiki. It is licensed under the LGPL.

Describes how to take SeamTest coverage in a multi-module Maven project.

See also SeamTest coverage (maven+cobertura)

Prerequisites

  1. You have a multi-module project with the following structure:
project
  project-ejb
  project-web
  project-ear
  project-common
  1. You have SeamTest's in project-web that test Actions and EJB's in project-ejb. (Having the tests in the web project makes it a lot easier to get JBoss embedded running, see JBSEAM-2371)
  2. You have the Maven Cobertura plugin properly setup. (see Cobertura)

The Problem

Your tests are passing, but you get no coverage for the classes in the ejb project.

Analysis

The Cobertura Maven Plugin instruments only the classes in the current sub-project ( in \target\generated-classes\cobertura) and uses those to take code coverage. By default instrumented classes from other sub-projects are not on the classpath, and you get no coverage for those.

To get coverage you need to put the instrumented classes from the relevant sub-projects (project-ejb in the above example) on the classpath for your SeamTests. Simply copying them over to project-web/target-classes doesn't work and getting the classpath right for JBoss embedded in Maven is already tricky. The easiest solution is to overwrite the project-ejb jar in your local maven repository with an instrumented one. After that you can execute your test as usual. The following section shows how to get the whole process automated.

Solution

We define a profile that instruments the ejb jar in place, aggregates the ejb module coverage with the SeamTests coverage (in the web module) and creates a report.

The cobertura maven plugin is not flexible enough to handle multiple projects, so we use the ant tasks via the AntRun plugin. Instrumenting the ejb jar in place might lead to packaging instrumented classes to your production ear, so we save the original file and restore it after the tests are done. (note that the script is not very smart: if your tests fail, you end up with an instrumented jar in your repository. You can make it smarter, but, hopefully, you wouldn't be deploying into production if tests are failing :))

Here's the profile we define:

<profile>
  <id>coverage</id>
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-antrun-plugin</artifactId>
        <dependencies>
          <dependency>
            <groupId>net.sourceforge.cobertura</groupId>
            <artifactId>cobertura</artifactId>
            <version>1.9</version>
          </dependency>
        </dependencies>
        <executions>
          <execution>
            <phase>process-test-classes</phase>
            <id>instrument-ejb-jar</id>
            <configuration>
              <tasks>
                <taskdef classpathref="maven.runtime.classpath"
                  resource="tasks.properties" />
                <mkdir dir="${project.build.directory}/cobertura" />
                <copy
                  todir="${project.build.directory}/cobertura">
                  <fileset
                    dir="../project-ejb/target/cobertura" />
                </copy>
                <copy
                  file="${settings.localRepository}/org/foobar/project-ejb/${project.version}/project-ejb-${project.version}.jar"
                  tofile="${settings.localRepository}/org/foobar/project-ejb/${project.version}/project-ejb-${project.version}.jar.org" />
                <cobertura-instrument>
                  <includeClasses regex=".*" />
                   <excludeClasses
                    regex=".*\DontLikeInstrumentation.*" />
                  <instrumentationClasspath>
                    <pathelement
                      location="${settings.localRepository}/org/foobar/project-ejb/${project.version}/project-ejb-${project.version}.jar" />
                  </instrumentationClasspath>

                    <!-- or use this:-->
                  <!--
                  <fileset
                    dir="${settings.localRepository}/org/foobar/project-ejb/${project.version}">
                    <include
                      name="project-ejb-${project.version}.jar" />
                  </fileset>
                  -->
                </cobertura-instrument>
              </tasks>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
          <execution>
            <phase>integration-test</phase>
            <id>cobertura-report</id>
            <configuration>
              <tasks>
                <taskdef classpathref="maven.runtime.classpath"
                  resource="tasks.properties" />
                <mkdir
                  dir="${project.build.directory}/site/cobertura" />
                <cobertura-report format="html"
                  datafile="${project.build.directory}/cobertura/cobertura.ser"
                  destdir="${project.build.directory}/site/cobertura">
                  <fileset dir="${basedir}/src/main/java">
                    <include name="**/*.java" />
                  </fileset>
                  <fileset
                    dir="../project-ejb/src/main/java">
                    <include name="**/*.java" />
                  </fileset>
                </cobertura-report>

                <copy overwrite="true"
                  file="${settings.localRepository}/org/foobar/project-ejb/${project.version}/project-ejb-${project.version}.jar.org"
                  tofile="${settings.localRepository}/org/foobar/project-ejb/${project.version}/project-ejb-${project.version}.jar" />
              </tasks>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</profile>

And here's how to use it:

  1. Run the tests and take coverage of the ejb project (mvn cobertura:cobertura)
  2. Go to the web project dir and execute mvn -Pcoverage integration-test
  3. The coverage report is output to project-web/target/site/cobertura

(almost) No Ant Solution

I've been tackling thins again and I decied to take Juan Pablo's suggestion and redo this using the jar and install Maven plugins. Here's what I came up with.

In project-ejb and project-common define the following profile:

<profiles>
    <profile>
        <id>coverage</id>
        <dependencies>
            <dependency>
                <groupId>net.sourceforge.cobertura</groupId>
                <artifactId>cobertura</artifactId>
                <optional>true</optional>
            </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>
</profiles>

What this does is instrumend your classes, jar them and install them in your local repository with the 'cobertura' classifier. By using a classifier for you instrumented classes, you can be sure that no instrumented classes end up in a production build.

In your project-web define the following profile:

 <profile>
    <id>coverage</id>
    <dependencies>
        <dependency>
            <groupId>net.sourceforge.cobertura</groupId>
            <artifactId>cobertura</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.foobar</groupId>
            <artifactId>project-ejb</artifactId>
            <version>1.0-SNAPSHOT</version>
            <classifier>cobertura</classifier>
        </dependency>
        <dependency>
            <groupId>org.foobar</groupId>
            <artifactId>project-comon</artifactId>
            <version>1.0-SNAPSHOT</version>
            <classifier>cobertura</classifier>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-antrun-plugin</artifactId>
                <dependencies>
                    <dependency>
                        <groupId>net.sourceforge.cobertura</groupId>
                        <artifactId>cobertura</artifactId>
                        <version>1.9</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <id>merge-cobertura-datafiles</id>
                        <phase>process-test-classes</phase>
                        <configuration>
                            <tasks>
                                <taskdef
                                    classpathref="maven.runtime.classpath"
                                    resource="tasks.properties" />
                                <cobertura-merge
                                    datafile="${project.build.directory}/cobertura/cobertura.ser">
                                    <fileset dir="../">
                                        <include
                                            name="project-ejb/target/cobertura/cobertura.ser" />
                                        <include
                                            name="project-common/target/cobertura/cobertura.ser" />
                                    </fileset>
                                </cobertura-merge>
                            </tasks>
                        </configuration>
                        <goals>
                            <goal>run</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
          </plugins>
    </build>
</profile>

This makes your build dependent on the instrumented classes (with 'cobertura' classifier) and merges the coverage data from the project-ejb and project-common modules. Unfortunately, the Maven cobertura plugin has no 'merge' goal, so we have to resort to Ant to do the merging.

How to use it:

  1. Run mvn -Pcoverage clean install in the top directory of the project.
  2. Run mvn -Pcoverage clean cobertura:cobertura.
  3. The merged coverage report is output to project-web/target/site/cobertura.

Notes

Apperantly, JBoss embedded doesn't like instrumented MDB's, so you may want to exclude those. Otherwise you may get the following error:

Caused by: java.lang.RuntimeException: Unable to choose messagingType i
for MDB TerminalStateChangedListener from [interface javax.jms.MessageL
interface net.sourceforge.cobertura.coveragedata.HasBeenInstrumented]