Missing jacoco.exec file when using jacoco offline

2020-07-24 06:05发布

问题:

Despite apparently this post showed a solution to using powermock and jacoco, I haven't been able to make it work in a pretty simple project (available on GitHub).

In my case, the test executes correctly but the jacoco.exec file is missing so jacoco doesn't check coverage.

Test class:

@RunWith(PowerMockRunner.class)
@PrepareOnlyThisForTest(Util.class)
@PowerMockIgnore("org.jacoco.agent.rt.*")
public class UtilTest {

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

}

Util.java

public class Util {

    private Util() {}

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

}

pom.xml

<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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.codependent.jacocopower</groupId>
    <artifactId>jacoco-powermock</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <build>
        <plugins>
            <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>${jacoco.version}</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>
                    <execution>
                        <id>default-check</id>
                        <goals>
                            <goal>check</goal>
                        </goals>
                        <configuration>
                            <rules>
                                <rule>
                                    <element>CLASS</element>
                                    <limits>
                                        <limit>
                                            <counter>LINE</counter>
                                            <value>COVEREDRATIO</value>
                                            <minimum>0.50</minimum>
                                        </limit>
                                    </limits>
                                </rule>
                            </rules>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

     <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>

    <properties>
        <powermock.version>1.5.4</powermock.version>
        <jacoco.version>0.7.4.201502262128</jacoco.version>
    </properties>

</project>

Maven execution trace, complaining that no jacoco.exec file was found:

>> mvn clean verify

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building jacoco-powermock 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ jacoco-powermock ---
[INFO] Deleting C:\SoftDesarrollo\6-Workspaces\libertyGecon\jacoco-powermock\target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ jacoco-powermock ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:2.5.1:compile (default-compile) @ jacoco-powermock ---
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 1 source file to C:\SoftDesarrollo\6-Workspaces\libertyGecon\jacoco-powermock\target\classes
[INFO]
[INFO] --- jacoco-maven-plugin:0.7.4.201502262128:instrument (default-instrument) @ jacoco-powermock ---
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ jacoco-powermock ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:2.5.1:testCompile (default-testCompile) @ jacoco-powermock ---
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 1 source file to C:\SoftDesarrollo\6-Workspaces\libertyGecon\jacoco-powermock\target\test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.18.1:test (default-test) @ jacoco-powermock ---
[INFO] Surefire report directory: C:\SoftDesarrollo\6-Workspaces\libertyGecon\jacoco-powermock\target\surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.codependent.jacoco.UtilTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.239 sec - in com.codependent.jacoco.UtilTest

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO]
[INFO] --- jacoco-maven-plugin:0.7.4.201502262128:restore-instrumented-classes (default-restore-instrumented-classes) @ jacoco-powermock ---
[INFO]
[INFO] --- jacoco-maven-plugin:0.7.4.201502262128:report (default-report) @ jacoco-powermock ---
[INFO] Skipping JaCoCo execution due to missing execution data file:C:\SoftDesarrollo\6-Workspaces\libertyGecon\jacoco-powermock\target\jacoco.exec
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ jacoco-powermock ---
[INFO] Building jar: C:\SoftDesarrollo\6-Workspaces\libertyGecon\jacoco-powermock\target\jacoco-powermock-0.0.1-SNAPSHOT.jar
[INFO]
[INFO] --- jacoco-maven-plugin:0.7.4.201502262128:check (default-check) @ jacoco-powermock ---
[INFO] Skipping JaCoCo execution due to missing execution data file:C:\SoftDesarrollo\6-Workspaces\libertyGecon\jacoco-powermock\target\jacoco.exec
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.987s
[INFO] Finished at: Thu Jan 21 09:13:55 CET 2016
[INFO] Final Memory: 23M/331M
[INFO] ------------------------------------------------------------------------

UPDATE: (here on GitHub)

With the answer provided by Lencalot the jacoco.exec file is generated but the following scenario keeps saying that the ServiceImpl class coverage is 0%:

ServiceImpl

public class ServiceImpl implements Service{

    public String operation() {
        return Util.say("Hi!");
    }

}

ServiceTest

@RunWith(PowerMockRunner.class)
@PrepareForTest({Util.class})
public class ServiceTest {

    private Service service = new ServiceImpl();

    @Test
    public void testOperation() throws Exception {
        PowerMockito.mockStatic(Util.class);
        Mockito.when(Util.say(Mockito.anyString())).thenReturn("Bye!");
        Assert.assertEquals("Bye!", service.operation());
    }

}

Trace:

[WARNING] Rule violated for class com.codependent.jacoco.ServiceImpl: lines covered ratio is 0.00, but expected minimum is 0.50

回答1:

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>${jacoco.version}</version>
    <configuration>
        <append>true</append>
    </configuration>
    <executions>
        <execution>
            <id>default-instrument</id>
            <goals>
                <goal>instrument</goal>
            </goals>
            <configuration>
                <includes>
                    <include>**/*test*</include>
                </includes>
            </configuration>
        </execution>
        <execution>
            <id>default-restore-instrumented-classes</id>
            <goals>
                <goal>restore-instrumented-classes</goal>
            </goals>
            <configuration>
                <includes>
                    <include>**/*test*</include>
                </includes>
            </configuration>
        </execution>
        <execution>
            <id>Prepare-Jacoco</id>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
            <configuration>
                <excludes>
                    <exclude>**/*test*</exclude>
                </excludes>
            </configuration>
        </execution>
    </executions>
</plugin>

you can remove Check for now, we can try to get it working with out that first.

Update your Jacoco dependency to look like this

<dependency>
    <groupId>org.jacoco</groupId>
    <artifactId>org.jacoco.agent</artifactId>
    <version>${jacoco.version}</version>
    <scope>test</scope>
</dependency> 

Make you maven surefire plugin look like this:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <argLine>${argLine}</argLine>
    </configuration>
</plugin>

in your properties section Add this as, and change your jacoco Version:

<jacoco.version>0.7.5.201505241946</jacoco.version>
<jacoco.outputDir>${project.basedir}/target/jacoco.exec</jacoco.outputDir>

@codependent Heres an example of why you want to mock.

Lets assume your Util.class has a lot more code, a lot of instantiations and maybe it even touches some server side stuff, hell maybe even its a UI. You don't want to instantiate User.class just to test a single method in some other class called UtilUser.class

protected class UtilUser {
    protected UtilUser() {}

    public Boolean checkSay(String s) {
        String expectedString = "hello:" + s;
        String actualString = Util.say(s);

        if (expectedString.equals(actualString){
           return true;
        } else { 
           return false;
        }

    }
}

To unit test UtilUser, you want to hit all branches. So in your test you would mock the Util.class, and in one unit test force it to return the correct string, and in another test have it return an incorrect string. You don't care about testing User.class, you know it will behave accordingly or you will have another test to take care of that. For unit testing UtilUser, you can expect Util to either return a correct string or an incorrect string, and you want to test both possibilities.

Theres a time and a place for mocks and they are a great tool to know, but DO NOT think they should be used often. Please refer to the following article, it will give you good insight into when you should use mocks: When To Mock