maven 多模块项目上的 cobertura

Posted

技术标签:

【中文标题】maven 多模块项目上的 cobertura【英文标题】:cobertura on maven multi module project 【发布时间】:2010-11-28 02:08:20 【问题描述】:

我有一个包含 4 个模块的 Maven 项目 - 其中 3 个包含代码和一些测试(测试类的 equals 和哈希码),而第 4 个模块用于测试其他 3 个模块。

现在我想运行 cobertura 代码覆盖率工具来大致了解哪些类经过了良好的测试,哪些没有。我对该主题进行了一些调查,如果某些经过测试的源位于其他模块中,cobertura 似乎不知道生成正确的代码覆盖率和行覆盖率。

我已经阅读了一些链接,例如 SeamTestCoverageWithCobertura 和 Using the plugin Coverage within a multi-module Maven 2,但必须有一个开箱即用的解决方案。有人可以报告有关此主题的一些新方向吗?或者有没有像 cobertura 这样的 bether 工具?我偶然发现了 emma,但这个工具不提供线路覆盖......

【问题讨论】:

【参考方案1】:

从 2.6 版开始,有一个聚合选项可以在父 pom 中设置为 true:

<reporting>
<plugins>
  <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>cobertura-maven-plugin</artifactId>
    <version>2.6</version>
    <configuration>
        <outputDirectory>./target/tmpCobertura</outputDirectory>
        <formats>
            <format>html</format>
        </formats>
        <aggregate>true</aggregate>
    </configuration>
  </plugin>
</plugins>
</reporting>

【讨论】:

恕我直言,这应该成为接受的答案,因为接受的答案中提到的两个问题自 2.5 以来都已修复。 这也可以仅通过命令行完成:mvn cobertura:cobertura -Dcobertura.aggregate=true -Dcobertura.report.format=xml 您可以根据需要更改报告格式。根据 cobertura maven 插件的 github repo,此功能可用 since v2.5 (commit 64a8823)。 但不确定为什么通过这种“聚合”方式覆盖结果总是 0 即使拥有 true 我也没有得到综合报告。我还应该验证什么?事实上,我的项目使用的是 cobertura 2.7 版,它声称默认解决了多模块项目问题。 在 cobertura 版本 2.7 中无法使用 aggregate=true 获得聚合覆盖率。有人知道设置有什么问题吗?【参考方案2】:

我们现在没有声纳,我们无法安装它。所以我必须找到一种解决方法并得到一个。此解决方案适用于多模块项目中的简单mvn clean install -DrunCobertura=true。您只需将此配置文件添加到您的项目的super pom.xml,定义working.dir 属性,它应该可以工作。

<profile>
    <id>runCobertura</id>
    <activation>
        <property>
            <name>runCobertura</name>
            <value>true</value>
        </property>
    </activation>
    <properties>
        <cobertura.format>html</cobertura.format>
        <cobertura.working.dir>$working.dir/$project.version/cobertura</cobertura.working.dir>
        <cobertura.complete.ser.file>$cobertura.working.dir/complete.ser</cobertura.complete.ser.file>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-clean-plugin</artifactId>
                <version>2.4.1</version>
                <inherited>false</inherited>
                <configuration>
                    <filesets>
                        <fileset>
                            <directory>.</directory>
                            <includes>
                                <include>cobertura.ser</include>
                            </includes>
                        </fileset>
                        <fileset>
                                <directory>$cobertura.working.dir</directory>
                            </fileset>
                    </filesets>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>1.7</version>
                <executions>
                    <execution>
                        <id>cobertura-Instrument</id>
                        <phase>process-classes</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <target>
                                <taskdef resource="tasks.properties"/>
                                <taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
                                <if>
                                    <available file="$project.build.outputDirectory"/>
                                    <then>
                                        <cobertura-instrument>
                                            <fileset dir="$project.build.outputDirectory">
                                                <include name="**/*.class"/>
                                            </fileset>
                                        </cobertura-instrument>
                                    </then>
                                </if>
                            </target>
                        </configuration>
                    </execution>
                    <execution>
                        <id>cobertura-createCombinedSerFile</id>
                        <phase>generate-test-sources</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <target>
                                <taskdef resource="tasks.properties"/>
                                <taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
                                <if>
                                    <available file="$cobertura.complete.ser.file"/>
                                    <then>
                                        <cobertura-merge datafile="$basedir/tmp.ser">
                                            <fileset file="$cobertura.complete.ser.file"/>
                                            <fileset file="$basedir/cobertura.ser"/>
                                        </cobertura-merge>
                                        <move file="$basedir/tmp.ser" tofile="$basedir/cobertura.ser"/>
                                    </then>
                                </if>
                            </target>
                        </configuration>
                    </execution>
                    <execution>
                        <id>cobertura-copyResultSerFileAndSources</id>
                        <phase>test</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <target>
                                <taskdef resource="tasks.properties"/>
                                <taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
                                <if>
                                    <available file="$basedir/cobertura.ser"/>
                                    <then>
                                        <move file="$basedir/cobertura.ser" tofile="$cobertura.complete.ser.file"/>
                                        <mkdir dir="$cobertura.working.dir/source"/>
                                        <if>
                                            <available file="$basedir/src/main/java"/>
                                            <then>
                                                <copy todir="$cobertura.working.dir/source">
                                                    <fileset dir="src/main/java">
                                                        <include name="**/*.java"/>
                                                    </fileset>
                                                </copy>
                                            </then>
                                        </if>
                                        <cobertura-report datafile="$cobertura.complete.ser.file" format="$cobertura.format" destdir="$cobertura.working.dir/report">
                                            <fileset dir="$cobertura.working.dir/source"/>
                                        </cobertura-report>
                                    </then>
                                </if>
                            </target>
                        </configuration>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>net.sourceforge.cobertura</groupId>
                        <artifactId>cobertura</artifactId>
                        <version>1.9.4.1</version>
                    </dependency>
                    <dependency>
                        <groupId>ant-contrib</groupId>
                        <artifactId>ant-contrib</artifactId>
                        <version>20020829</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>net.sourceforge.cobertura</groupId>
            <artifactId>cobertura</artifactId>
            <version>1.9.4.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</profile>

它是做什么的:

1. @process-classes-检测模块的编译类。

2. @generate-test-sources-将来自先前模块的.ser 文件与创建的此模块之一合并以获得完整的代码覆盖率。

3. @test-创建代码覆盖率报告。应该在最后一个模块中调用,但由于最后一个模块可以更改,我总是调用它并且以前的报告将被覆盖。如果您使用xml 格式的报告(对于 Jenkins),它很快,所以没关系。

【讨论】:

【参考方案3】:

根据MCOBERTURA-65 的说法,maven cobertura 插件仍然不知道如何将子模块的报告聚合成一个合并的报告。已经完成了一些工作以在 maven cobertura 插件上实现 merge 目标(请参阅 MCOBERTURA-33),但此代码尚未包含在插件中。我自己没有测试补丁,也不能说它是否值得一试。

因此,确实有很多人建议使用 maven dashboard plugin,但我个人会远离它,因为从长远来看它并不是很令人满意,而且我遇到了很多问题(技术问题,失去了历史,...)。相反,我强烈推荐Sonar。查看Nemo,Sonar 最新版本的公共实例,查看该工具的现场演示。例如,参见 Commons Digester 项目和 drill down of code coverage。

【讨论】:

这就是我添加 Nemo 链接的原因。检查例如 nemo.sonarsource.org/project/index/… 和向下钻取:nemo.sonarsource.org/drilldown/measures/51834?metric=coverage Sonar 不会显示前三个模块中的代码被第四个模块中的代码覆盖。它只是汇总了 4 个完全独立且不完整的报告。 据我了解,Sonar 不允许您根据阈值控制代码覆盖率,作为构建生命周期的一部分..【参考方案4】:

有一些插件可以汇总 Cobertura(和其他)报告。查看sonar 和XRadar 插件。还有dashboard plugin,不过有点笨重。

FWIW Emma 确实做到了line coverage。

【讨论】:

【参考方案5】:

非常感谢 Sven Oppermann 提交他的 runCobertura 配置文件解决方案。这有助于 我解决了“当你可能不是时如何获得多模块项目的综合覆盖率报告”的问题 可以使用声纳。

我创建了一个示例,该示例演示了如何创建生成代码覆盖率报告的多模块项目,该报告不仅评估单元测试覆盖率(在所有子模块中),还评估带来的集成测试的覆盖率 将您的应用程序升级为 .WAR 在 JetTY。该示例在此处托管:

        http://dl.dropbox.com/u/9940067/code/multi-module-cobertura.zip 

如果您复制下面列出的 runCobertura 配置文件(基于 由 Sven 提供。)

以下是一些有助于您使用此个人资料的注意事项:

启动码头的集成测试模块(并定义运行的测试 生产 .war) 必须命名为 web-test-driver-for-code-coverage,或者您 必须修改 runCobertura 配置块中的语句。

您的覆盖率报告将出现在您设置变量的任何位置

当您运行构建以进行代码覆盖时,您必须在命令行中包含“clean”。 'clean' 会清除之前的 cobertura.ser 文件, 如果留下潜伏可能会导致非常混乱的报告 生成(您需要“清理”的标志是报告显示 100% 覆盖所有内容,包括您知道从未调用过的内容。

  mvn -PrunCobertura clean install      # gives you the aggregate reports.

模块 web-test-driver-for-code-coverage 定义了一个 servlet 上下文侦听器,该侦听器将 cobertura 指标显式刷新到磁盘 当网络服务器关闭时。假设容器应该自动执行此操作,但这对我不起作用,所以 我必须加入显式调用以清除指标。

集成测试是在 groovy 中完成的,因为我基于一些已经使用 groovy 的 maven 项目框架。 很抱歉增加了混乱,但它确实向您展示了如何在 groovy 中进行测试(无论如何强烈推荐。)

请注意,当您使用 runCobertura 配置文件进行编译时,您的所有工件都是使用 cobertura 工具创建的,甚至您的 .war 文件。当然,您永远不想让它在生产中出现(一方面它会运行得很慢。)我没有 却想出了一种食物方法来让这些文物重命名,这样“cobertura-ness”就很明显了。

<profiles>
<profile>
    <id>runCobertura</id>
    <activation>
        <property>
            <name>runCobertura</name>
            <value>true</value>
        </property>
    </activation>
    <properties>
        <cobertura.format>html</cobertura.format>
        <working.dir>/tmp</working.dir>
        <cobertura.working.dir>$working.dir/$project.version/cobertura</cobertura.working.dir>
        <cobertura.complete.ser.file>$cobertura.working.dir/complete.ser</cobertura.complete.ser.file>

        <!-- scope which determines whether or not cobertura is included in .war file: overriden here -->
        <cobertura.dependency.scope>compile</cobertura.dependency.scope>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-clean-plugin</artifactId>
                <version>2.4.1</version>
                <inherited>false</inherited>
                <configuration>
                    <filesets>
                        <fileset>
                            <directory>.</directory>
                            <includes>
                                <include>**/cobertura.ser</include>
                            </includes>
                        </fileset>
                        <fileset>
                                <directory>$cobertura.working.dir</directory>
                            </fileset>
                    </filesets>
                </configuration>
            </plugin>




            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>1.7</version>
                <executions>
                    <execution>
                        <id>cobertura-Instrument</id>
                        <phase>process-classes</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <target>
                                <taskdef resource="tasks.properties"/>
                                <taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
                                <echo message="::PROCESS CLASSES: $artifactId"/>

                                <if>
                                  <equals arg1="$artifactId" arg2="web-test-driver-for-code-coverage" />
                                    <then>
                                        <echo message="::SKIPPING PHASE for integration test"/>
                                    </then>
                                    <else>
                                        <if>
                                            <available file="$project.build.outputDirectory"/>
                                            <then>
                                                <echo message="::BEFORE INSTRUMENT"/>
                                                <cobertura-instrument>
                                                    <fileset dir="$project.build.outputDirectory">
                                                        <include name="**/*.class"/>
                                                    </fileset>
                                                </cobertura-instrument>
                                            </then>
                                        </if>
                                    </else>
                                </if>


                            </target>
                        </configuration>
                    </execution>
                    <execution>
                        <id>cobertura-createCombinedSerFile</id>
                        <phase>generate-test-sources</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <target>
                                <taskdef resource="tasks.properties"/>
                                <taskdef resource="net/sf/antcontrib/antcontrib.properties"/>
                                <echo message=":::generate-test-sources"/>


                                <if>
                                  <equals arg1="$artifactId" arg2="web-test-driver-for-code-coverage" />
                                    <then>
                                        <echo message="::SHORT CIRCUIT COMBINE PHASE for integration test"/>
                                        <echo  message="source - $cobertura.complete.ser.file dest - $basedir/cobertura.ser"/>
                                        <copy file="$cobertura.complete.ser.file" tofile="$basedir/cobertura.ser"/>
                                    </then>
                                    <else>
                                        <if>
                                            <available file="$basedir/cobertura.ser"/>
                                            <then>
                                                <echo message="::: Is available $basedir/cobertura.ser"/>
                                            </then>
                                        </if>

                                        <if>
                                            <available file="$cobertura.complete.ser.file"/>
                                            <then>
                                                <echo message="before merge1"/>
                                                <cobertura-merge datafile="$basedir/tmp.ser">
                                                    <fileset file="$cobertura.complete.ser.file"/>
                                                    <fileset file="$basedir/cobertura.ser"/>
                                                </cobertura-merge>
                                                <echo message="move temp.ser to $basedir/cobertura.ser"/>
                                                <move file="$basedir/tmp.ser" tofile="$basedir/cobertura.ser"/>
                                            </then>
                                        </if>
                                    </else>
                                </if>
                            </target>
                        </configuration>
                    </execution>
                    <execution>
                        <id>cobertura-copyResultSerFileAndSources</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <target>
                                <taskdef resource="tasks.properties"/>
                                <taskdef resource="net/sf/antcontrib/antcontrib.properties"/>

                                <echo message=":::copyResultSerFileAndSources -beforeIf"/>
                                <if>
                                    <available file="$basedir/cobertura.ser"/>
                                    <then>
                                        <echo message="move1"/>
                                        <move file="$basedir/cobertura.ser" tofile="$cobertura.complete.ser.file"/>
                                        <mkdir dir="$cobertura.working.dir/source"/>
                                        <if>
                                            <available file="$basedir/src/main/java"/>
                                            <then>
                                                <copy todir="$cobertura.working.dir/source">
                                                    <fileset dir="src/main/java">
                                                        <include name="**/*.java"/>
                                                    </fileset>
                                                </copy>
                                            </then>
                                        </if>
                                        <echo message="runreport"/>
                                        <cobertura-report datafile="$cobertura.complete.ser.file" format="$cobertura.format" destdir="$cobertura.working.dir/report">
                                            <fileset dir="$cobertura.working.dir/source"/>
                                        </cobertura-report>
                                    </then>
                                </if>
                            </target>
                        </configuration>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>net.sourceforge.cobertura</groupId>
                        <artifactId>cobertura</artifactId>
                        <version>1.9.4.1</version>
                    </dependency>
                    <dependency>
                        <groupId>ant-contrib</groupId>
                        <artifactId>ant-contrib</artifactId>
                        <version>20020829</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>net.sourceforge.cobertura</groupId>
            <artifactId>cobertura</artifactId>
            <version>1.9.4.1</version>
        </dependency>
    </dependencies>
</profile>
</profiles>

【讨论】:

【参考方案6】:

Thomas Sundberg 提供了一个有趣的解决方案,其中检测和测试报告通过ant 完成,但所有测试和依赖管理通过mvn 完成。

在这里查看: thomassundberg wordpress

表示你必须在父级按照这个顺序执行下面的命令:

mvn clean compile
ant instrument
mvn test
ant report

Martijn Stelinga 描述了将这些步骤集成到 sonar 中。

test-coverage-in-multi-module-projects

【讨论】:

【参考方案7】:

感谢这个答案,我可以实现与您需要的非常相似的东西:Maven - add dependency on artifact source

我刚刚添加了&lt;classifier&gt;sources&lt;/classifier&gt;,cobertura 也包含依赖项中的类。

问候。

【讨论】:

以上是关于maven 多模块项目上的 cobertura的主要内容,如果未能解决你的问题,请参考以下文章

使用 maven 运行 junits 和 cobertura

Maven 找不到 maven-plugins:maven-cobertura-plugin

如何使用来自 Hudson 的 Maven 生成 Cobertura 代码覆盖率报告

spring && Cobertura && maven &&junit 单元测试以及测试覆盖率

通过 Maven 的单元测试,但通过 Cobertura 失败:“在分支目标 65 处期望堆栈图帧”

当 cobertura 测试在 maven-cobertura-plugin 中运行时如何配置?