NoSuchMethodError: org.junit.platform.commons.util.ReflectionUtils.tryToLoadClass

NoSuchMethodError: org.junit.platform.commons.util.ReflectionUtils.tryToLoadClass

我有导致错误的测试。我尝试在 IntelliJ Idea 2018.3.2 中执行它。所有 jupiter 和 junit 依赖项都有版本 RELEASE

错误全文:

Dec 26, 2018 1:17:17 AM org.junit.platform.launcher.core.DefaultLauncher handleThrowable
WARNING: TestEngine with ID 'junit-jupiter' failed to execute tests
java.lang.NoSuchMethodError: org.junit.platform.commons.util.ReflectionUtils.tryToLoadClass(Ljava/lang/String;)Lorg/junit/platform/commons/function/Try;
at org.junit.jupiter.engine.support.OpenTest4JAndJUnit4AwareThrowableCollector.createAbortedExecutionPredicate(OpenTest4JAndJUnit4AwareThrowableCollector.java:40)
at org.junit.jupiter.engine.support.OpenTest4JAndJUnit4AwareThrowableCollector.<clinit>(OpenTest4JAndJUnit4AwareThrowableCollector.java:30)
at org.junit.jupiter.engine.support.JupiterThrowableCollectorFactory.createThrowableCollector(JupiterThrowableCollectorFactory.java:34)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:68)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:220)
at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute(DefaultLauncher.java:188)
at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:202)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:181)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

测试有如下视图

import biz.Services.msg.BookTimeMsgService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertTrue;

@ExtendWith(MockitoExtension.class)
public class MsgReceiverTest {

@Mock
BookTimeMsgService bookTimeMsgService;

@InjectMocks
MsgReceiver msgReceiver;

@Test
public void msgReceiverTest_bookTimeServiceShouldObtainMsg() {
    assertTrue(true);
}

我的一部分 pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.1.RELEASE</version>
</parent>

   <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>RELEASE</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.junit</groupId>
        <artifactId>junit-bom</artifactId>
        <version>RELEASE</version>
        <type>pom</type>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-junit-jupiter</artifactId>
        <version>RELEASE</version>
        <scope>test</scope>
    </dependency>

如何解决这个问题?

我把jupiter和junit的版本改成5.3.2,问题没有了

添加平台和启动器依赖项后,我能够运行 Junit 5 测试:

<!-- https://mvnrepository.com/artifact/org.junit.platform/junit-platform-commons -->
<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-commons</artifactId>
    <version>1.4.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.junit.platform/junit-platform-launcher -->
<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-launcher</artifactId>
    <version>1.4.0</version>
    <scope>test</scope>
</dependency>

pom.xml spring-boot 2.1.3 和 junit 5.4.0 接下来测试的最低要求 pom.xml 设置:

<?xml version="1.0" encoding="UTF-8"?>
<project ...>
  <parent>
    <groupId>org.springframework.boot</g..roupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.3.RELEASE</version>
    <relativePath/>
  </parent>

  <properties>
    <junit-jupiter.version>5.4.0</junit-jupiter.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

使用:

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.4.2</version>
    <scope>test</scope>
</dependency>

改为

<dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <scope>test</scope>
</dependency>
<dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <scope>test</scope>
</dependency>

您可以找到更多详细信息here

Maven/Gradle、Eclipse/IntelliJ 的组合使用 Spring Boot/or 而不是包含 Junit4/Junit5 会抛出不同类型的 NoSuchMethodError。这导致许多不同的解决方案,所有这些解决方案只能解决一部分人的问题。这个冗长的答案试图解释根本原因以及您可以采取的解决问题的步骤,无论您使用的是哪种口味。

根本原因:
在编译时,使用了一个版本的 ReflectionUtils(我在这里也看到了其他 classes),而在 运行 时,使用了另一个版本。 运行time 版本期望某个方法存在,但它要么不存在,要么存在但具有不同的签名(number/type 个参数)

注意,当我说编译时间时,这可能意味着(并且通常确实)意味着用于在您的 class 路径中编译第三方 jar 的 ReflectionUtils 版本。

解决方案总是更改或排除某些 jar 的某些版本。诀窍是找到哪个 jar 和哪个版本。

求解:
首先,确定在 运行 时间使用的 ReflectionUtils(或它抱怨的其他 class)的版本。这很容易。在 eclipse 中,做一个 Open Type,在 IntelliJ 中,使用 navigate/go 到 class。确保完全限定路径。如果路径上恰好有 1 个版本,这很好。如果路径上有多个版本,您要做的第一个操作是修改构建路径以排除其中一个版本。

如果您无法修改构建路径,即使是暂时的,也很难确定在 运行 时使用的是哪个版本的 ReflectionUtils。由于 JUnit 4 和 5 在您可以 运行 任何代码之前开始和结束,因此您不能插入

CodeSource src = ReflectionUtils.class.getProtectionDomain().getCodeSource();
if (src != null) {
    URL jar = src.getLocation();
    System.out.println(jar.toString());
}

获取 运行时间罐的位置。不过你可以试试:

1 - 创建一个全新的新项目,一个全新的 pom,将 1 个 junit 放入其中,然后 运行 使用上面的代码查看结果。这可能有效,因为非工作项目中的任何冲突都已被删除。这可能不起作用,在这种情况下转到#2。这也可能会给出错误的结果,因为新项目的 运行 时间路径可能与非工作项目的时间路径不同。尝试更新新项目的 pom,使其看起来越来越像非工作项目的 pom。请记住,目标是找到 运行 时间

正在使用的 class 版本

2 - 使用 google。是的,我知道您是通过使用 google 来到这里的,但这次更改您的搜索以查看是否有人记录了 "This version of JUnit5 uses this version of ReflectionUtils at runtime" 或 "This version of IntelliJ puts this version of ReflectionUtils on the classpath" 或 "This version of Spring Boot is putting this version of ReflectionUtils on the classpath"。请记住,目标是找到 运行 时间

正在使用的 class 版本

3 - 劫持 class(高级)。当stacktrace出来的时候,你会在stacktrace中看到几个classes。尝试在 Internet 上找到 class 的源代码,并在您的项目中创建该包和 class。然后插入上面的代码并尝试 运行 失败的测试。

一旦找到 运行 时使用的 ReflectionUtils 版本,您需要找出编译时使用的版本

确定哪个 jar ReflectionUtils 位于其中。

在您 运行 测试时弹出的堆栈跟踪中,确定顶部 class 和尝试使用 ReflectionUtils 时死亡的方法。这是使用不兼容版本的 ReflectionUtils 编译的代码。找出 class 里面的罐子。我将这个罐子称为冲突罐子。尝试为那个冲突的 jar 获取一个依赖树。依赖关系树应该指出冲突的 jar 依赖于哪个版本的 ReflectionUtils jar。

一旦您发现在编译时和 运行 时使用了哪个版本的 ReflectionUtils jar,您可能会发现它们不同并且不兼容。此时,您将不得不

1 - 更改 运行 时使用的 ReflectionUtils jar 版本以匹配冲突 jar

所需的 ReflectionUtils 版本

2 - 将冲突 jar 的版本更改为依赖于 运行时间路径

上相同版本的 ReflectionUtils jar 的版本

3 - 如果冲突的 jar 是构建路径中某些父 jar 的依赖项,并且未明确包含在您的构建路径中,请尝试从父 jar 中排除冲突的 jar 并明确添加更新的版本。这可能会解决问题——但这当然是首先造成这种混乱的行为类型。

您可以采取的其他行动:
如果您知道在项目中同时使用 JUnit4 jar 和 JUnit5,请尝试从编译时和 运行time 路径中完全删除其中一个。这两种范例的断言模式差异很大,您可能不希望将它们混合在同一个项目中,因为这可能导致混淆哪些参数是预期结果、消息和被测变量。