无法让Jacoco使用离线工具与Powermockito合作

时间:2014-06-01 19:42:44

标签: java powermock jacoco

鉴于Jacoco在“动态”检测时不能很好地与PowerMockito配合使用,我一直在尝试配置离线检测,希望这能为我提供适用于使用PowerMockito的类的单元测试覆盖率。

我已经设置了我的pom,但我的测试课程仍然保持0%的覆盖率。任何帮助都非常感激,因为它让我慢慢疯狂!

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>mandy</groupId>
    <artifactId>jacoco-test</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>jacoco-test Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <properties>
        <powermock.version>1.5.4</powermock.version>
        <jacoco.version>0.7.1.201405082137</jacoco.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.jacoco</groupId>
            <artifactId>org.jacoco.agent</artifactId>
            <classifier>runtime</classifier>
            <version>${jacoco.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-module-junit4</artifactId>
            <version>${powermock.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-api-mockito</artifactId>
            <version>${powermock.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>

    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>${jacoco.version}</version>
                <executions>
                    <execution>
                        <id>instrument</id>
                        <phase>process-classes</phase>
                        <goals>
                            <goal>instrument</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>restore-report</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>restore-instrumented-classes</goal>
                            <goal>report</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.17</version>
                <configuration>
                    <!--<argLine>${argLine}</argLine>-->
                    <systemPropertyVariables>
                        <!-- JaCoCo runtime must know where to dump coverage: -->
                        <jacoco-agent.destfile>target/jacoco.exec</jacoco-agent.destfile>
                    </systemPropertyVariables>
                </configuration>
            </plugin>
        </plugins>
        <finalName>jacoco-test</finalName>
    </build>
</project>

这是我的课程:

public class Utils {

    private Utils() {

    }

    public static String say(String s) {
        return "hello:"+s;
    }
}

这是我的测试:

@RunWith(PowerMockRunner.class)

@PrepareOnlyThisForTest(Utils.class)
@PowerMockIgnore("org.jacoco.agent.rt.*")
public class UtilsTest {

    @Test
    public void testSay() throws Exception {
        PowerMockito.mockStatic(Utils.class);
        Mockito.when(Utils.say(Mockito.anyString())).thenReturn("hello:mandy");
        assertEquals("hello:mandy", Utils.say("sid"));
    }

}

我运行mvn clean install生成jacoco.exe

覆盖率报告(使用ant脚本从jacoco.exec生成): -

Coverage report

12 个答案:

答案 0 :(得分:14)

这个pom对我有用:

 <build>
    <finalName>final-name</finalName>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.18.1</version>
            <configuration>
                <systemPropertyVariables>
                    <jacoco-agent.destfile>target/jacoco.exec</jacoco-agent.destfile>
                </systemPropertyVariables>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <version>0.7.2.201409121644</version>
            <executions>
                <execution>
                    <id>default-instrument</id>
                    <goals>
                        <goal>instrument</goal>
                    </goals>
                </execution>
                <execution>
                    <id>default-restore-instrumented-classes</id>
                    <goals>
                        <goal>restore-instrumented-classes</goal>
                    </goals>
                </execution>
                <execution>
                    <id>default-report</id>
                    <phase>prepare-package</phase>
                    <goals>
                        <goal>report</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

请参阅this链接。

答案 1 :(得分:7)

I saw the same behavior, though after following the thread on the GitHub issue it seems to be fixed in 1.6.5, which proved true for me.

Hopefully this will save someone a headache later :).

Working configuration with:

  • jacoco-maven-plugin 0.7.7.201606060606
  • powermock 1.6.5

I am not using offline instrumentation.

答案 2 :(得分:1)

我使用PowerMock的javaagent使其工作。 见这里:https://code.google.com/p/powermock/wiki/PowerMockAgent

删除@RunWith注释,按照上面的链接中所述放置 PowerMockRule 。把它公之于众。

将以下行放在maven-surefire-plugin配置中:

-javaagent:${org.powermock:powermock-module-javaagent:jar}

(使用此处描述的技术:Can I use the path to a Maven dependency as a property?

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.stackoverflow</groupId>
    <artifactId>q2359872</artifactId>
    <version>2.0-SNAPSHOT</version>
    <name>q2359872</name>

    <properties>
        <!-- Must be listed in the dependencies section otherwise it will be null. -->
        <my.lib>${org.jmockit:jmockit:jar}</my.lib>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.jmockit</groupId>
            <artifactId>jmockit</artifactId>
            <version>1.11</version>
        </dependency>
    </dependencies>
    <build>
        <defaultGoal>generate-sources</defaultGoal>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.3</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>properties</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <!-- Example usage: -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                        <phase>generate-sources</phase>
                    </execution>
                </executions>
                <configuration>
                    <executable>echo</executable>
                    <arguments>
                        <argument>path to jar=</argument>
                        <argument>${org.jmockit:jmockit:jar}</argument>
                        <argument>my.lib=</argument>
                        <argument>${my.lib}</argument>
                    </arguments>
                </configuration>
            </plugin>
            <!-- end of Example usage -->
        </plugins>
    </build>
</project> 

答案 3 :(得分:1)

我也面临同样的问题。我能够部分生成报告。我已将这两个标签用于我的测试用例@RunWith(PowerMockRunner.class) @PrepareForTest({})。并且我没有为我使用上述标签的测试案例生成报告。但是对于其中一个测试用例,只有@RunWith(PowerMockRunner.class)这个标签。不知何故,报告是针对该案例生成的。而且我从未使用过离线仪器。当我尝试使用离线工具时,我收到错误,说该课程已经过检测。我尝试了各种方案,并遵循各种链接,但无法生成报告。最后根据上面的评论我将我的powermock版本从1.5.5升级到1.6.5,我能够生成报告。以下是我的 pom.xml 条目

     <plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.7.5.201505241946</version>
    <executions>
        <execution>
            <id>pre-unit-test</id>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
             <configuration>
                    <!-- Sets the path to the file which contains the execution data. -->
                    <destFile>${basedir}/target/jacoco.exec</destFile>
                    <!--
                        Sets the name of the property containing the settings
                        for JaCoCo runtime agent.
                    -->
            </configuration>
        </execution>
        <execution>
            <!--<id>post-unit-test</id>
            <phase>test</phase>-->
            <id>default-report</id>
            <phase>verify</phase>
            <goals>
                <goal>report</goal>
            </goals>
              <configuration>
                <!-- Sets the path to the file which contains the execution data. -->
                <dataFile>${basedir}/target/jacoco.exec</dataFile>
                <!-- Sets the output directory for the code coverage report. -->
                <outputDirectory>${basedir}/target/jacoco-ut</outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>

以下是我在 pom .xml中输入的maven-surefire-plugin

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
      <configuration>
            <argLine>@{argLine}</argLine>
            <skipTests>false</skipTests>
            <testFailureIgnore>true</testFailureIgnore>
        </configuration>
    </plugin>

@ {argLine}已设为属性

<properties>
    <argLine>-noverify -Xms512m -Xmx1024m -XX:PermSize=128m -XX:MaxPermSize=512m</argLine>
</properties>

并将我的powermock版本从1.5.5升级到1.6.5 。最后,我可以在我的测试用例中看到我使用以下标记的类生成报告 @RunWith(PowerMockRunner.class)@PrepareForTest({})

答案 4 :(得分:1)

我尝试了以下软件的这些版本;他们一起工作,无需任何额外工作即可生成jacoco报告。我将gradle构建工具与powermock,mockito和wiremock一起使用。

powermock-api-mockito2:2.0.2
powermock-module-junit4:2.0.2
gradle 5.6.2

以上内容正确生成了jacoco覆盖率报告。

答案 5 :(得分:0)

我最终使用离线工具和Jacoco(类似于你所做的)与sonar一起使用,我能够从中获得覆盖率数据。

答案 6 :(得分:0)

我使用了Jacoco离线工具,并在执​​行测试恢复原始类之后,在#34; restore-instrumented-classes&#34;目标。我的JaCoCo配置如下所示:

 <plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.7.9</version>
    <executions>
      <execution>
        <id>default-prepare-agent</id>
        <goals>
          <goal>prepare-agent</goal>
        </goals>
      </execution>
      <execution>
        <id>jacoco-instrument</id>
        <goals>
          <goal>instrument</goal>
        </goals>
      </execution>
      <execution>
        <id>jacoco-restore-instrumented-classes</id>
        <goals>
          <goal>restore-instrumented-classes</goal>
        </goals>
      </execution>
      <execution>
        <id>jacoco-report</id>
        <phase>package</phase>
        <goals>
          <goal>report</goal>
        </goals>
      </execution>
    </executions>
    <configuration>
      <excludes>
        <exclude>*</exclude>
      </excludes>
    </configuration>
  </plugin>

答案 7 :(得分:0)

我和JaCoCo On-the-fly和PowerMock有同样的问题。每次都会生成0%的代码覆盖率

发现JaCoCo版本0.7.7.201606060606和PowerMock版本1.6.2兼容,并且代码覆盖率已成功生成。

答案 8 :(得分:0)

使用下面的maven插件代码片段,这在jenkins和本地工作正常,并显示PowermockRunner单元测试的完整代码覆盖率

<build>
            <plugins>
                <plugin>
                    <groupId>org.jacoco</groupId>
                    <artifactId>jacoco-maven-plugin</artifactId>
                    <version>${jacoco-maven-plugin.version}</version>
                    <executions>
                        <execution>
                            <id>pre-test</id>
                            <goals>
                                <goal>prepare-agent</goal>
                            </goals>
                            <configuration>
                                <append>true</append>                           
                            </configuration>
                        </execution>
                        <execution>
                            <id>pre-integ-test</id>
                            <goals>
                                <goal>prepare-agent-integration</goal>
                            </goals>
                            <configuration>
                                <append>true</append>                           
                            </configuration>
                        </execution>

                        <execution>
                            <id>jacoco-instrument</id>
                            <goals>
                                <goal>instrument</goal>
                            </goals>
                        </execution>
                        <execution>
                            <id>jacoco-restore-instrumented-classes</id>
                            <goals>
                                <goal>restore-instrumented-classes</goal>
                            </goals>
                        </execution>
                    </executions>
                    <configuration>
                        <excludes>
                            <exclude>*</exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>

答案 9 :(得分:0)

对我来说,Offline Instrumentation的示例效果很好。

但就我而言,有一个更简单的解决方案:只是在声明之前,不要在@PrepareForTest({})批注中包含经过测试的类。

答案 10 :(得分:0)

Code Coverage with JaCoCo解释了原因

  

使用JaCoCo的最简单方法是-使用JaCoCo Java Agent进行即时检测。在这种情况下,加载类时会对其进行修改。您可以只使用JaCoCo代理运行应用程序,然后计算代码覆盖率。 Eclemma和Intellij Idea使用这种方式。但是有一个大问题。 PowerMock乐器类也有。 Javassist用于修改类。主要问题是Javassist从磁盘读取类,而所有JaCoCo更改都消失了。结果,PowerMock类加载器加载了巫婆类的零代码覆盖率。

     

我们将用ByteBuddy(#727)替换Javassist,它将帮助解决这个老问题。但是,现在无法将PowerMock与JaCoCo即时仪器一起使用。而且没有解决方法可以在IDE中获得代码覆盖。

JaCoCo Offline Instrumentation可以解决此问题。 Offline Instrumentation的示例解决了我的问题。

答案 11 :(得分:0)

该报告是正确的。当您模拟调用方法“ say”时发生的情况时,提供的测试不会导致任何覆盖。永远不要嘲笑“被测实例/类”,只能针对测试对象的依赖关系和环境进行模拟。

在这种情况下:测试永远不会到达代码return "hello:"+s;,并且不会调用Utils的构造函数。没有覆盖率显示,因为这些行根本没有测试覆盖率。

尽管如此,但同时使用JaCoCo和PowerMock并非易事。我目前也在寻找如何使其运行的方法。也许您提供的测试只是不幸地编写了,而JaCoCo已经可以正常工作了?