«1. Обзор
В этой статье мы поймем, почему в JUnit возникает ошибка java.lang.NoClassDefFoundError и как ее исправить. Эта проблема в основном связана с конфигурациями IDE. Поэтому мы сосредоточимся на самых популярных IDE: Visual Studio Code, Eclipse и IntelliJ, чтобы воспроизвести и устранить эту ошибку.
2. Что такое java.lang.NoClassDefFoundError?
Когда среда выполнения Java запускает программу Java, она не загружает сразу все классы и зависимости. Вместо этого он вызывает загрузчик классов Java для загрузки классов в память по мере необходимости. Если при загрузке класса загрузчик классов не может найти определение класса, он выдает ошибку NoClassDefFoundError.
Есть несколько причин, по которым Java не может найти определение класса, а именно:
-
Отсутствует несколько зависимых jar-файлов, что является наиболее распространенной причиной. Все банки добавляются как зависимости, но по неправильному пути. Несоответствие версий в зависимостях.
3. Код VS
Для написания тестовых примеров Junit4 нам потребуется jar-файл Junit4. Однако Junit4 имеет внутреннюю зависимость от jar ядра hamcrest.
Если мы пропустим добавление jar hamcrest-core в качестве зависимости в наш путь к классам, Java выдаст ошибку NoClassDefFoundError. Путь к классам выглядит следующим образом:
Еще один сценарий: мы добавили обе банки, но их версии не совпадают. Например, если мы добавили JUnit jar версии 4.13.2 и jar hamcrest-core версии 2.2, выдается ошибка NoClassDefFoundError:
В обоих случаях выводится одна и та же трассировка стека:
Чтобы устранить ошибку в обоих сценариях (отсутствующие зависимости и несоответствие версий), нам нужно добавить правильные зависимости. Правильные зависимости для Junit4: junit-4.13.2.jar и hamcrest-core-1.3.jar. Добавление этих двух банок в зависимости (ссылочные библиотеки) устраняет ошибку. Инструкции по добавлению и удалению внешних банок в VS Code представлены здесь. Раздел библиотеки, на который мы ссылаемся, должен быть настроен следующим образом:
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)
...
4. Eclipse
В Eclipse IDE, которая поддерживает Java 9 и выше, у нас есть путь к классам и путь к модулю. Чтобы разрешить зависимость модуля, мы используем путь к модулю. Однако добавление внешних jar-файлов в путь к модулю не делает их доступными для загрузчика классов. Следовательно, загрузчик классов считает их отсутствующими зависимостями и выдает ошибку NoClassDefFoundError.
Следовательно, если наша зависимость выглядит так, как показано на изображении ниже, выполнение тестового примера Junit приводит к ошибке NoClassDefFoundError:
Трассировка стека, сгенерированная при запуске теста JUnit:
В Eclipse нам нужно добавить банки в пути к классам, а не в пути к модулю. Итак, чтобы корректно добавить внешние jar-ы, следуйте по пути:
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)
правой кнопкой мыши на Project -\u003e Build Path -\u003e Configure Build Path
В открывшемся окне убираем jar-ы из-под пути к модулю и добавляем их под classpath. Это устраняет ошибку NoClassDefFoundError. Правильный путь к классам для запуска JUnit должен быть примерно таким:
5. IntelliJ
Для запуска тестовых случаев JUnit 5 требуется как механизм Jupiter, так и Jupiter API. Механизм Jupiter внутренне зависит от API Jupiter, поэтому в большинстве случаев достаточно добавить только зависимость механизма Jupiter в pom.xml. Однако добавление только зависимости Jupiter API в наш 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>
В IntelliJ, чтобы исправить зависимости , нам нужно исправить файл pom.xml. Исправленный pom.xml выглядит следующим образом:
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)
....
В качестве альтернативы мы можем добавить junit-jupiter-engine, так как его добавление автоматически добавляет jar junit-jupiter-api в путь к классам и устраняет ошибку.
<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>
6. Резюме
В этой статье мы рассмотрели различные причины возникновения ошибки java.lang.NoClassDefFoundError в JUnit. Мы также видели, как мы устраняем ошибку в разных IDE. Весь код для этого руководства доступен на GitHub.
«