gpt4 book ai didi

java - Log4j Logger.getLogger(Class) 在使用 jMockit 和 Cobertura 运行时抛出 NPE

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:47:57 26 4
gpt4 key购买 nike

我发现 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) 运行时,它会抛出顶部显示的令人讨厌的异常集。显然是某处的错误,但是谁的错误?

最佳答案

这个问题的原因与其说是一个错误,不如说是 JMockit 在模拟包含静态初始化程序的类时缺乏健壮性。下一版本的 JMockit (1.9) 将在这一点上进行改进(我已经有一个可行的解决方案)。

此外,如果 Cobertura 将其生成的方法(其中四个名称以“__cobertura_”开头,添加到每个检测类)标记为“合成的”,那么问题就不会发生,这样 JMockit 在模拟一个Cobertura 仪表类。无论如何,幸运的是这不是必需的。

目前,有两个简单的解决方法可以避免这个问题:

  1. 确保在测试开始时任何要模拟的类都已由 JVM 初始化。这可以通过实例化它或对其调用静态方法来完成。
  2. 将模拟字段或模拟参数声明为 @Mocked(stubOutClassInitialization = true)

这两种解决方法都可以防止 NPE 否则会从静态类初始化器内部抛出,该初始化器由 Cobertura 修改(要查看这些字节码修改,您可以使用 JDK 的 javap 工具,在 target/generated-classes 目录下的类上)。

关于java - Log4j Logger.getLogger(Class) 在使用 jMockit 和 Cobertura 运行时抛出 NPE,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23761091/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com