1. Обзор
В этой статье мы поймем, почему в JUnit возникает ошибка java.lang.NoClassDefFoundError
и как ее исправить. Эта проблема в основном связана с конфигурациями IDE. Поэтому мы сосредоточимся на самых популярных IDE: Visual Studio Code, Eclipse и IntelliJ, чтобы воспроизвести и устранить эту ошибку.
2. Что такое java.lang.NoClassDefFoundError
?
Когда среда выполнения Java запускает программу Java, она не загружает сразу все классы и зависимости. Вместо этого он вызывает загрузчик классов Java для загрузки классов в память по мере необходимости. При загрузке класса, если загрузчик классов не может найти определение класса, он выдает ошибку NoClassDefFoundError
.
Есть несколько причин, по которым Java не может найти определение класса:
- Отсутствие нескольких зависимых банок, что является наиболее распространенной причиной.
- Все банки добавляются как зависимости, но по неправильному пути.
- Несоответствие версий в зависимостях .
3. Код ВС
Для написания тестовых случаев Junit4 нам требуется jar Junit4. Однако Junit4 имеет внутреннюю зависимость от jar ядра hamcrest
.
Если мы пропустим добавление jar hamcrest-core
в качестве зависимости в наш путь к классам, Java выдаст ошибку NoClassDefFoundError
. Путь к классам выглядит следующим образом:
Еще один сценарий: мы добавили обе банки, но версии не совпадают. Например, если мы добавили JUnit jar версии 4.13.2 и jar версии 2.2 hamcrest-core ,
выдается NoClassDefFoundError
:
В обоих случаях выводится одна и та же трассировка стека:
java.lang.NoClassDefFoundError: org/hamcrest/SelfDescribing
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1010)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150)
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:855)
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:753)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:676)
...
Чтобы устранить ошибку в обоих сценариях (отсутствующие зависимости и несоответствие версий), нам нужно добавить правильные зависимости. В случае с Junit4 правильными зависимостями являются junit-4.13.2.jar
и hamcrest-core-1.3.jar
. Добавление этих двух банок в зависимости (ссылочные библиотеки) устраняет ошибку. Инструкции по добавлению и удалению внешних jar-файлов в VS Code присутствуют здесь . Раздел библиотеки, на который мы ссылаемся, должен быть настроен как:
4. Затмение
В среде Eclipse IDE, которая поддерживает Java 9 и выше, у нас есть путь к классам и путь к модулю. Чтобы разрешить зависимость модуля, мы используем путь к модулю. Однако добавление внешних jar-файлов в путь к модулю не делает их доступными для загрузчика классов . Следовательно, загрузчик классов считает их отсутствующими зависимостями и выдает ошибку NoClassDefFoundError
.
Следовательно, если наша зависимость выглядит так, как показано на изображении ниже, выполнение тестового примера Junit приводит к NoClassDefFoundError:
Трассировка стека, сгенерированная при выполнении теста JUnit, выглядит следующим образом:
java.lang.NoClassDefFoundError: org/junit/runner/manipulation/Filter
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:377)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.loadTestLoaderClass(RemoteTestRunner.java:381)
В Eclipse нам нужно добавить jar-файлы в путь к классам, а не в путь к модулю. Итак, чтобы правильно добавить внешние банки, следуйте по пути:
щелкните правой кнопкой мыши проект -> Путь сборки -> Настроить путь сборки
В открывшемся окне удалите банки из-под пути к модулю и добавьте их в путь к классам. Это устраняет NoClassDefFoundError
. Правильный путь к классам для запуска JUnit должен быть похож на:
5. Интеллидж
Для запуска тестовых случаев JUnit 5 требуется как механизм Jupiter, так и API Jupiter. Механизм Jupiter внутренне зависит от API Jupiter, поэтому в большинстве случаев достаточно добавить только зависимость механизма Jupiter в pom.xml. Однако добавление только зависимости API Jupiter в наш pom.xml
и отсутствие зависимости механизма Jupiter приводит к NoClassDefFoundError
.
Неправильная настройка в pom.xml
будет выглядеть так:
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
Запуск простого тестового примера с этой настройкой приводит к следующей трассировке стека:
Exception in thread "main" java.lang.NoClassDefFoundError: org/junit/platform/engine/TestDescriptor
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:375)
at com.intellij.rt.junit.JUnitStarter.getAgentClass(JUnitStarter.java:230)
....
В IntelliJ для исправления зависимостей нам нужно исправить pom.xml
. Исправленный pom.xml
выглядит так:
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
В качестве альтернативы мы можем добавить junit-jupiter-engine,
поскольку его добавление автоматически добавляет jar junit-jupiter-api
в путь к классам и устраняет ошибку.
6. Резюме
В этой статье мы рассмотрели различные причины возникновения ошибки java.lang.NoClassDefFoundError
в JUnit. Мы также видели, как мы устраняем ошибку в разных IDE. Весь код этого руководства доступен на GitHub .