Как включить полезные исключения NullPointerException в тесте gradle

Эта проблема была исправлена ​​в 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 или ошибка. Вот что я сделал:

Я настроил пустой проект Gradle с помощью этого build.gradle.kts

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:+ShowCodeDetailsInExceptionMessages появляется полезное сообщение 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

person apflieger    schedule 07.05.2020    source источник
comment
Это файл сборки kotlin или groovy?   -  person smac89    schedule 08.05.2020
comment
Kotlin это, как это в моем build.gradle.kts   -  person apflieger    schedule 08.05.2020
comment
Вам нужно убедиться, что ваш jvm поддерживает эту опцию. Если вы включите эту деталь в свой вопрос, будет легче узнать, как помочь   -  person smac89    schedule 08.05.2020
comment
Отвечает ли это на ваш вопрос? java 14 nullpointerexception без подробного сообщения   -  person smac89    schedule 08.05.2020
comment
Я отредактировал вопрос, добавив java -версию и тот факт, что я проверил, что опция работает на моем java   -  person apflieger    schedule 09.05.2020
comment
Можете ли вы показать, какое сообщение об ошибке вы получаете при запуске теста?   -  person smac89    schedule 09.05.2020
comment
Я считаю, что причина, по которой это не работает, вероятно, заключается в том, что ваша среда тестирования обрабатывает сообщение об исключении нулевого указателя до того, как JVM получит возможность его обработать. Вероятно, поэтому, когда вы запускаете свое обычное приложение, вы получаете сообщение jvm, потому что ничто не обрабатывает исключение   -  person smac89    schedule 09.05.2020
comment
Я провел дополнительные исследования и отредактировал вопрос в разделе ИЗМЕНИТЬ С ДОПОЛНИТЕЛЬНЫМИ ИССЛЕДОВАНИЯМИ. Я мог бы исключить jvm и тестовую среду   -  person apflieger    schedule 10.05.2020
comment
Возможно, проблема не конкретно в Gradle, а в задаче Gradle Test. я посмотрю на это   -  person smac89    schedule 10.05.2020
comment
Это что-то с тестовой задачей, тот же код показывает сообщение NPE с tomcat в моем случае   -  person cocorossello    schedule 11.05.2020


Ответы (1)


Это проблема Gradle, она будет работать с Gradle 6.6.

Можешь попробовать:

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

Подробное сообщение находится в распечатанной трассировке стека, но его нет в сообщении об ошибке junit.

Проблема связана с gradle, я считаю, что он использует Object Input/Output Stream для передачи результатов выполнения из test 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.junit.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;
}

}

person cocorossello    schedule 11.05.2020
comment
Отлично как обходной путь, спасибо. Это все еще звучит как ошибка, я открыл проблему github.com/gradle/gradle/issues/13096. - person apflieger; 12.05.2020
comment
Он будет работать в грядущей версии Gradle 6.6, см. github.com/gradle/gradle/pull/13193< /а> - person cocorossello; 29.06.2020
comment
@apflieger, можете ли вы принять этот ответ, если он ответил/решил вашу проблему? - person Matthieu; 28.01.2021
comment
Хорошо, проблема была исправлена ​​​​в Gradle - person apflieger; 09.02.2021