JUnit中java.lang.NoClassDefFoundError的错误及其解决方法
JUnit中java.lang.NoClassDefFoundError的错误及其解决方法
在本文中,我们将理解为什么JUnit中会出现_java.lang.NoClassDefFoundError_错误以及如何修复它。这个问题主要与IDE的配置有关。因此,我们将专注于最流行的IDE:Visual Studio Code、Eclipse和IntelliJ,来重现和解决这个错误。
当Java运行时运行Java程序时,它不会一次性加载所有类和依赖项。相反,它调用Java类加载器按需加载类到内存中。在加载类时,如果类加载器找不到类的声明,它会抛出_NoClassDefFoundError_。
Java找不到类定义有几个原因,包括:
- 缺少一些依赖jar,这是最常见的原因。
- 所有jar都添加为依赖,但路径错误。
- 依赖项中的版本不匹配。
3. VS Code
对于编写JUnit4测试用例,我们需要JUnit4 jar。然而,JUnit4对_hamcrest-core_ jar有内部依赖。
如果我们在类路径中错过了将_hamcrest-core_ jar作为依赖项添加,Java就会抛出_NoClassDefFoundError_。类路径如下所示: 
另一种情况是,我们添加了这两个jar,但版本不匹配。例如,如果我们添加了JUnit jar版本4.13.2和_hamcrest-core_ jar版本2.2,就会抛出_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中添加和删除外部jar的说明在这里。我们的引用库部分应该设置为: 
4. Eclipse
在支持Java 9及以上版本的Eclipse IDE中,我们有一个类路径和一个模块路径。要解决模块依赖,我们使用模块路径。然而,在模块路径中添加外部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添加到类路径而不是模块路径下。因此,要正确添加外部jar,请按照路径操作: 右键点击项目 -> 构建路径 -> 配置构建路径
在打开的窗口中,从模块路径下移除jar,并将它们添加到类路径下。这将解决_NoClassDefFoundError_。运行JUnit的正确类路径应该是类似的: 
5. IntelliJ
**运行JUnit 5测试用例需要Jupiter引擎和Jupiter API。**Jupiter引擎内部依赖于Jupiter API,因此,大多数时候,在pom.xml中只添加Jupiter引擎依赖就足够了。然而,如果我们在_pom.xml_中只添加了Jupiter API依赖,而错过了Jupiter引擎依赖,就会得到_NoClassDefFoundError_。
_pom.xml_中的不正确设置如下:
``<dependencies>``
```<dependency>```
```<groupId>```org.junit.jupiter```</groupId>```
```<artifactId>```junit-jupiter-api```</artifactId>```
```<version>```5.11.0-M2```</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.11.0-M2```</version>```
```<scope>```test```</scope>```
```</dependency>```
```<dependency>```
```<groupId>```org.junit.jupiter```</groupId>```
```<artifactId>```junit-jupiter-engine```</artifactId>```
```<version>```5.11.0-M2```</version>```
```<scope>```test```</scope>```
```</dependency>```
``</dependencies>``
或者,我们可以添加_junit-jupiter-engine_,因为添加它会自动将_junit-jupiter-api_ jar添加到类路径中,并解决错误。
6. 总结
在本文中,我们看到了JUnit中发生_java.lang.NoClassDefFoundError_的不同原因。我们还看到了如何在不同的IDE中解决这个错误。本教程的全部代码可在GitHub上找到。