解决Jacoco和PowerMock不兼容的问题
Posted 朱清云的技术博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解决Jacoco和PowerMock不兼容的问题相关的知识,希望对你有一定的参考价值。
在使用PowerMock来写单元测试的时候,且单元测试里面下面的@PrepareForTest和@RunWith(PowerMockRunner.class)的时候,单元测试能成功跑出来,但是其生成的单元测试覆盖率为零。比如下面的代码:
package org.powermock.example;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
import java.util.ArrayList;
import java.util.Properties;
import static org.assertj.core.api.Assertions.assertThat;
import static org.powermock.api.mockito.PowerMockito.doCallRealMethod;
import static org.powermock.api.mockito.PowerMockito.doReturn;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;
@RunWith(PowerMockRunner.class)
@PrepareForTest(Configuration.class, Lamda.class)
public class SomeClassTest
@Before
public void setUp() throws Exception
Whitebox.setInternalState(Configuration.class, "enabled", (Object) null);
@Test
public void shouldReturnSumIfEnabled() throws Exception
mockStatic(Configuration.class);
Properties properties = new Properties();
properties.put("enabled", "true");
doReturn(properties).when(Configuration.class, "readProperties");
doCallRealMethod().when(Configuration.class, "isEnabled");
doCallRealMethod().when(Configuration.class, "loadFromProperties");
assertThat(new SomeClass().add(1, 5)).isEqualTo(6);
@Test
public void shouldReturnZeroIfDisabled() throws Exception
mockStatic(Configuration.class);
Properties properties = new Properties();
properties.put("enabled", "false");
doReturn(properties).when(Configuration.class, "readProperties");
doCallRealMethod().when(Configuration.class, "isEnabled");
doCallRealMethod().when(Configuration.class, "loadFromProperties");
assertThat(new SomeClass().add(1, 5)).isEqualTo(0);
@Test(expected = RuntimeException.class)
public void shouldC()
mockStatic(Lamda.class);
when(Lamda.capitalize(Mockito.anyString())).thenReturn("01234567890");
ArrayList<String> in = new ArrayList<>();
in.add("----");
in.add(null);
new Lamda().validate(in);
package org.powermock.example;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import lombok.Data;
@Data
public class Configuration
private String name;
private static Boolean enabled;
public static boolean isEnabled()
if (enabled == null)
loadFromProperties();
return enabled;
private static void loadFromProperties()
Properties properties = readProperties();
enabled = "true".equals(properties.getProperty("enabled"));
private static Properties readProperties()
Properties properties = new Properties();
try
properties.load(new FileInputStream("some.properties"));
catch (IOException e)
e.printStackTrace();
return properties;
package org.powermock.example;
import java.util.List;
import java.util.function.Consumer;
public class Lamda
public static String capitalize(String in)
String result = "";
String[] a = in.split(",");
for (String s : a)
result += s.substring(0, 1).toUpperCase() + s.substring(1, s.length());
return result;
public void validate(List<String> in)
doWithList(in, s ->
if (s != null && s.length() > 10)
throw new RuntimeException("");
);
private void doWithList(List<String> in, Consumer<String> consumer)
in.stream().map(Lamda::capitalize).forEach(consumer);
package org.powermock.example;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
/**
*
*/
@Data
public class SomeClass
public int add(int x, int y)
if (Configuration.isEnabled())
return x + y;
return 0;
那么应该如何做呢?Pom.xml的配置应该注意哪些点呢?
#1. 如果pom.xml还继承了parent pom,确保的你的parent pom里面没有plugin的配置
否则会报下面的类似的错误。
aused by: java.lang.IllegalStateException: Class xxxx.class is already instrumented.
at org.jacoco.agent.rt.internal_290345e.core.internal.instr.InstrSupport.assertNotInstrumented(InstrSupport.java:176)
at org.jacoco.agent.rt.internal_290345e.core.internal.instr.ClassInstrumenter.visitField(ClassInstrumenter.java:55)
at org.jacoco.agent.rt.internal_290345e.asm.ClassVisitor.visitField(ClassVisitor.java:294)
at org.jacoco.agent.rt.internal_290345e.asm.ClassReader.readField(ClassReader.java:883)
at org.jacoco.agent.rt.internal_290345e.asm.ClassReader.accept(ClassReader.java:694)
at org.jacoco.agent.rt.internal_290345e.asm.ClassReader.accept(ClassReader.java:500)
at org.jacoco.agent.rt.internal_290345e.core.instr.Instrumenter.instrument(Instrumenter.java:90)
at org.jacoco.agent.rt.internal_290345e.core.instr.Instrumenter.instrument(Instrumenter.java:108)
#2 确保你的dependency中有下面的依赖
<dependency>
<groupId>org.jacoco</groupId>
<artifactId>org.jacoco.agent</artifactId>
<version>$jacoco.version</version>
<classifier>runtime</classifier>
</dependency>
否则会报下面的错误:
com.siact.product.jwp.module.user.service.UserTest Time elapsed: 0.073 sec <<< ERROR!
java.lang.NoClassDefFoundError: org/jacoco/agent/rt/internal_290345e/Offline
Caused by: java.lang.ClassNotFoundException: org.jacoco.agent.rt.internal_290345e.Offline
#3 确保你的maven-surefire-plugin的生成的acoco-agent.destfile值和jacoco-maven-plugin中的dataFile一致
#4 确保你的PowerMock的版本和Mockito的版本能匹配上,符合下面的列表
#5 确保你的是用了org.jacoco offline的模式。
<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>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<dataFile>$project.build.directory/coverage.exec</dataFile>
</configuration>
</execution>
</executions>
</plugin>
下面上整个pom.xml的文件:
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>org.powermock</groupId>
<artifactId>powermock-examples</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>jacoco-offline</artifactId>
<name>JaCoCo Offline with PowerMock</name>
<description>
Example how to get code coverage with PowerMock
</description>
<properties>
<jacoco.version>0.7.7.201606060606</jacoco.version>
<!-- Used to locate the profile specific configuration file. -->
<build.profile.id>dev</build.profile.id>
<jacoco.it.execution.data.file>$project.build.directory/coverage-reports/jacoco-it.exec</jacoco.it.execution.data.file>
<jacoco.ut.execution.data.file>$project.build.directory/coverage-reports/jacoco-ut.exec</jacoco.ut.execution.data.file>
<jdk.version>1.8</jdk.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- Only unit tests are run by default. -->
<skip.unit.tests>false</skip.unit.tests>
</properties>
<dependencies>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
</dependency>
<dependency>
<groupId>org.powermock<Android 单元测试实战—— 基于Cobertra&sonarqube的单元测试覆盖率统计
Android 单元测试实战—— 基于Cobertra&sonarqube的单元测试覆盖率统计