1. 概述

在本文中,我们将了解为什么JUnit中会出现 java.lang.NoClassDefFoundError 以及如何修复它。这个问题主要与IDE的配置有关。因此,我们将重点关注最流行的 IDE:Visual Studio Code、Eclipse 和 IntelliJ 来重现并解决此错误。

2. 什么是 java.lang.NoClassDefFoundError

当Java运行时运行Java程序时,它不会立即加载所有类和依赖项。相反,它调用 Java 类加载器根据需要将类加载到内存中。 加载类时,如果类加载器找不到类的定义,则会抛出 NoClassDefFoundError

Java 找不到类的定义有以下几个原因:

  • 缺少一些依赖的 jar 是最常见的原因。
  • 所有 jar 都作为依赖项添加,但路径错误。
  • 依赖项中的版本不匹配**。**

3.VS代码

为了编写 Junit4 测试用例,我们需要 Junit4 jar。但是, Junit4 对 hamcrest-core jar 有内部依赖性。

如果我们错过将 hamcrest-core jar 添加为类路径中的依赖项,Java 会抛出 NoClassDefFoundError 。类路径如下:

j1

另一种情况是我们添加了两个 jar,但版本不匹配。例如,如果我们添加了 JUnit jar 版本 4.13.2 和 hamcrest-core jar 版本 2.2,则会抛出 NoClassDefFoundError

j2

在这两种情况下,都会打印相同的堆栈跟踪:

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.jarhamcrest-core-1.3.jar 。在依赖项(引用库)中添加这两个 jar 可以解决该错误。此处提供了在 VS Code 中添加和删除外部 jar 的说明。我们引用的库部分应设置为:

j3

4.日食

在支持 Java 9 及更高版本的 Eclipse IDE 中,我们有一个类路径和一个模块路径。为了解决模块依赖性,我们使用模块路径。但是, 在模块路径中添加外部 jar 并不能使它们可供类加载器使用 。因此,类加载器将它们视为缺少依赖项并抛出 NoClassDefFoundError

因此,如果我们的依赖项如下图所示,则运行 Junit 测试用例会导致 NoClassDefFoundError:

eclipse模块路径

运行 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 的正确类路径应类似于:

eclipse正确设置

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.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 ,因为添加它会自动将 junit-jupiter-api jar 添加到类路径并解决错误。

六、总结

在本文中,我们了解了 JUnit 中发生 java.lang.NoClassDefFoundError 的不同原因。我们还了解了如何解决不同 IDE 中的错误。本教程的完整代码可在 GitHub 上获取。