我发现cobertura-maven-plugin 2.6和jmockit 1.8之间存在奇怪的互动。我们的生产代码中的一个特定模式有一个类,它有很多静态方法,可以有效地包装一个像单例一样的不同类。在我尝试使用cobertura运行覆盖率报告时,这些类的写入单元测试很顺利,当出现此错误时:
java.lang.ExceptionInInitializerError
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
Caused by: java.lang.NullPointerException
at com.example.foo.MySingleton.<clinit>(MySingleton.java:7)
... 13 more
然后导致NoClassDefFoundError
并且无法初始化单例类。这是一个完整的SSCCE(最短的我可以得到它)复制错误; MySingleton
的第7行是Logger.getLogger()
。
这是“单身人士”......
package com.example.foo;
import org.apache.log4j.Logger;
public class MySingleton {
private static final Logger LOG = Logger.getLogger(MySingleton.class);
private boolean inited = false;
private Double d;
MySingleton() {
}
public boolean isInited() {
return inited;
}
public void start() {
inited = true;
}
public double getD() {
return d;
}
}
静态类......
package com.example.foo;
import org.apache.log4j.Logger;
public class MyStatic {
private static final Logger LOGGER = Logger.getLogger(MyStatic.class);
private static MySingleton u = new MySingleton();
public static double getD() {
if (u.isInited()) {
return u.getD();
}
return 0.0;
}
}
打破一切的测试......
package com.example.foo;
import mockit.Expectations;
import mockit.Mocked;
import mockit.Tested;
import org.junit.Test;
public class MyStaticTest {
@Tested MyStatic myStatic;
@Mocked MySingleton single;
@Test
public void testThatBombs() {
new Expectations() {{
single.isInited(); result = true;
single.getD(); /*result = 1.2;*/
}};
// Deencapsulation.invoke(MyStatic.class, "getD");
MyStatic.getD();
}
}
maven pom:
<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.example.foo</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Test</name>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.8</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<version>2.6</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
总结总结:当运行普通单元测试(mvn clean test
)时,上面的测试就好了;当使用cobertura(mvn clean cobertura:cobertura
)运行时,它会抛出顶部显示的令人讨厌的异常集。显然是某个地方的错误,但是谁的?
答案 0 :(得分:3)
这个问题的原因并不是一个错误,而是在模拟包含静态初始化程序的类时JMockit缺乏健壮性。下一版本的JMockit(1.9)将在这一点上得到改进(我已经有了一个可行的解决方案)。
此外,如果Cobertura将其生成的方法(其中四个名称以“__cobertura_”开头,添加到每个已检测的类中)标记为“合成”,则问题不会发生,因此JMockit在模拟时会忽略它们Cobertura仪器化课程。无论如何,幸运的是,这不是必要的。
目前,有两种简单的解决方法可以避免这个问题:
@Mocked(stubOutClassInitialization = true)
。这两种解决方法都会阻止NPE从静态类初始值设定项中抛出,而初始化程序由Cobertura修改(要查看这些字节码修改,您可以在类上使用JDK的javap
工具在target/generated-classes
目录下。