如何在渐变测试中启用有用的NullPointerExceptions



此问题已在Gradle 6.6中修复

原始帖子

我想设置标志-XX:+ShowCodeDetailsInExceptionMessages以启用有用的NPE(https://openjdk.java.net/jeps/358)关于测试

我试过

tasks.withType<Test> {
jvmArgs("-XX:+ShowCodeDetailsInExceptionMessages")
testLogging {
setExceptionFormat("full") // Prints the message of the exception
}
}

但NPE仍然没有任何信息。

这是我的java版本

java -version
openjdk version "14.0.1" 2020-04-14
OpenJDK Runtime Environment (build 14.0.1+7)
OpenJDK 64-Bit Server VM (build 14.0.1+7, mixed mode, sharing)

编辑更多研究

为了更准确地找出问题的根源,我又做了一些工作。我的结论是,它既不是来自我的java,也不是来自测试框架,它只能是gradle或bug的错误配置。以下是我所做的:

我用这个build.gradle.kts 设置了一个空的gradle项目

plugins {
java
}
repositories {
mavenCentral()
}
dependencies {
testImplementation("junit:junit:4.13")
}
tasks.withType<Test> {
jvmArgs("-XX:+ShowCodeDetailsInExceptionMessages") // Helpful NPEs not working :(
testLogging {
setExceptionFormat("full") // Prints exception messages
}
}

使用测试类(src/test/java/TestNPE.java(

import org.junit.*;
public class TestNPE {
@Test
public void true_npe() {
Object o = null;
o.toString();
}
@Test
public void throw_npe() {
throw new NullPointerException("My own message");
}
}

所以现在

./gradlew test
> Task :test FAILED
TestNPE > throw_npe FAILED
java.lang.NullPointerException: My own message
at TestNPE.throw_npe(TestNPE.java:13)
TestNPE > true_npe FAILED
java.lang.NullPointerException
at TestNPE.true_npe(TestNPE.java:8)
2 tests completed, 2 failed

这意味着该框架对NPE没有任何特殊作用。

之后,我通过使用调试日志运行测试来检索gradle使用的类路径。这样,我就可以直接运行JUnit。使用-XX:+ShowCodeDetailsInException消息,会显示有用的NPE消息:

java -cp '/Users/apflieger/src/gradle-helpfull-npe/build/classes/java/test:/Users/apflieger/.gradle/caches/modules-2/files-2.1/junit/junit/4.13/e49ccba652b735c93bd6e6f59760d8254cf597dd/junit-4.13.jar:/Users/apflieger/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar' -XX:+ShowCodeDetailsInExceptionMessages org.junit.runner.JUnitCore TestNPE
JUnit version 4.13
.E.E
Time: 0.006
There were 2 failures:
1) throw_npe(TestNPE)
java.lang.NullPointerException: My own message
at TestNPE.throw_npe(TestNPE.java:13)
2) true_npe(TestNPE)
java.lang.NullPointerException: Cannot invoke "Object.toString()" because "o" is null
at TestNPE.true_npe(TestNPE.java:8)
FAILURES!!!
Tests run: 2,  Failures: 2

这是一个gradle问题,它将从gradle 6.6开始工作。

你可以试试:

@Test
public void true_npe() {
try {
Object o = null;
o.toString();
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}

详细消息在打印的堆栈跟踪中,但不在junit错误消息中

问题来自gradle,我相信它使用对象输入/输出流将执行结果从测试vm传递到gradle vm。在该交换过程中,来自原始NPE的扩展消息丢失(这是一种本地方法(。

您可以使用自定义的junit 5扩展来解决此问题(应该与junit 4类似(:

public class HelpfulNullPointerExceptionExtension implements TestExecutionExceptionHandler {
@Override
public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {
if (throwable instanceof NullPointerException) {
NullPointerException extended = new NullPointerException(throwable.getMessage());
extended.setStackTrace(throwable.getStackTrace());
throw extended;
}
throw throwable;
}
}

并在META-INF/services/org.unit.jupiter.api.extension.extension文件中自动注册,内容为

org.mypackage.HelpfulNullPointerExceptionExtension 

或者,如果你关心嵌套的NPE:

public class HelpfulNullPointerExceptionExtension implements TestExecutionExceptionHandler {
@Override
public void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {
Throwable actual = throwable;
do {
if (actual instanceof NullPointerException) {
var field = Throwable.class.getDeclaredField("detailMessage");
field.setAccessible(true);
field.set(actual, actual.getMessage());
}
actual = actual.getCause();
} while (actual != null && actual != throwable);
throw throwable;
}

}

相关内容

  • 没有找到相关文章

最新更新