Перейти к основному содержимому

Ошибка java.lang.NoClassDefFoundError в JUnit

· 4 мин. чтения

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 . Путь к классам выглядит следующим образом:

./685441e1beea1fe95e9e054afa31b2a0.png

Еще один сценарий: мы добавили обе банки, но версии не совпадают. Например, если мы добавили JUnit jar версии 4.13.2 и jar версии 2.2 hamcrest-core , выдается NoClassDefFoundError :

./862ff58c14b49a983ebc12f5e8e8a6f4.png

В обоих случаях выводится одна и та же трассировка стека:

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 присутствуют здесь . Раздел библиотеки, на который мы ссылаемся, должен быть настроен как:

./99ff93b85cc66ec7a0f8d0b46cb6d2d0.png

4. Затмение

В среде Eclipse IDE, которая поддерживает Java 9 и выше, у нас есть путь к классам и путь к модулю. Чтобы разрешить зависимость модуля, мы используем путь к модулю. Однако добавление внешних jar-файлов в путь к модулю не делает их доступными для загрузчика классов . Следовательно, загрузчик классов считает их отсутствующими зависимостями и выдает ошибку NoClassDefFoundError .

Следовательно, если наша зависимость выглядит так, как показано на изображении ниже, выполнение тестового примера Junit приводит к NoClassDefFoundError:

./59b911c40f6cd58786077157e86e4723.png

Трассировка стека, сгенерированная при выполнении теста 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 должен быть похож на:

./c23dc5d91eb7b5c7b56ea39daefe2505.png

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 .