IT/Java

Solve ClassNotFoundException: org.junit.platform.engine.EngineDiscoveryListener

yeTi 2020. 3. 24. 14:26

안녕하세요. yeTi입니다.
오늘은 Eclipse에서 JUnit을 구동할때 발생하는 Caused by: java.lang.ClassNotFoundException: org.junit.platform.engine.EngineDiscoveryListener를 해결한 상황을 공유하고자 합니다.

저는 다음과 같은 상황에서 정상적으로 JUnit을 사용하고 있었습니다.

  • IDE : STS-4.5.1.RELEASE
  • Java : openjdk-12.0.1 (JavaSE-11)
  • SpringBoot : 2.2.4.RELEASE
  • Gradle : 6.0.1
  • JUnit : 5.5.2

오류

그런데 STS에서 업데이트 안내가 떠서 업데이트를 하고나니(4.6.0.RELEASE) 다음과 같은 오류가 발생하면서 JUnit이 동작하지 않았습니다.

java.lang.NoClassDefFoundError: org/junit/platform/engine/EngineDiscoveryListener
    at java.base/java.lang.ClassLoader.defineClass1(Native Method)
    at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
    at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:151)
    at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:802)
    at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:700)
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:623)
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
    at org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.getLauncherDiscoveryListener(LauncherDiscoveryRequestBuilder.java:241)
    at org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.build(LauncherDiscoveryRequestBuilder.java:235)
    at org.eclipse.jdt.internal.junit5.runner.JUnit5TestLoader.createFilteredTest(JUnit5TestLoader.java:70)
    at org.eclipse.jdt.internal.junit5.runner.JUnit5TestLoader.createTest(JUnit5TestLoader.java:64)
    at org.eclipse.jdt.internal.junit5.runner.JUnit5TestLoader.loadTests(JUnit5TestLoader.java:53)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:526)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:770)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
Caused by: java.lang.ClassNotFoundException: org.junit.platform.engine.EngineDiscoveryListener
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
    ... 18 more

잘 동작하던게 오류가 나온 상황이라 의아한 생각에 차근차근 디버깅을 해봤습니다.

원인 분석

일단 org.junit.platform.engine.EngineDiscoveryListener를 찾아보니 junit-platform-engine-1.5.2.jar내에 존재하지 않는것을 확인하였고, JUnit5 - Docs를 확인한 결과 1.6부터 추가된것을 알수 있었습니다.

그리고는 혹시 이 문제가 STS의 버전에 따른 문제인지 확인해보기 위해 다음과 같이 테스트를 해봤습니다.

  • Gradle Test로는 정상 동작함
  • 신규 프로젝트의 빌드툴을 Gradle로 만들어서 확인하니 동일한 현상이 발생하는 것을 확인
  • 신규 프로젝트의 빌드툴을 Maven으로 변경해서 확인하니 정상 동작함
  • 이전 버전인 STS-4.4.1.RELEASE를 다운받아서 프로젝트를 새로 만드니 정상 동작함
  • 현재 버전인 STS-4.6.0.RELEASE, Eclipse-2020-03에서 다운받아서 프로젝트를 새로 만드니 동일한 현상이 발생하는 것을 확인

이처럼 STS에 Gradle을 사용한 경우에 발생하는 것을 확인했고, STS의 버전업과 관련이 있는것을 확인했습니다.

그렇다면, 왜 업그레이드된 STS에서 1.6을 찾고 있을까요? STS에서 JUnit의 버전이 변경됐을까요?

해결

구글링 결과, EclipseでJUnit5がjava.lang.NoClassDefFoundError: org/junit/platform/engine/EngineDiscoveryListenerになる- Qiita에서 제시한대로 testRuntimeOnly 'org.junit.platform:junit-platform-launcher'의존성을 추가했더니 정상 동작하는것을 확인했습니다.

상세 분석

junit-platform-launcher의 버전을 1.6.0으로 변경하면 문제 상황이 동일하게 발생하는것을 확인하였으므로 junit의 launcher의 변경에 따른 문제로 볼 수 있습니다.

STS의 버전별 차이점을 확인해보니 eclipse plugins에 존재하는 org.junit.platform.launcher의 버전 차이가 있었습니다.

  • STS-4.4.1.RELEASE(기존) : 1.5.1
  • STS-4.6.0.RELEASE(최신) : 1.5.1, 1.6.0
  • Eclipse-2020-03(최신) : 1.6.0

이클립스가 JUnit을 구동하는 절차를 보면 다음과 같습니다.

  • org.eclipse.jdt.junit.runtime플러그인에 있는 org.eclipse.jdt.internal.junit.runner.RemoteTestRunner클래스 구동

    • org.eclipse.jdt.junit5.runtime플러그인에 있는 org.eclipse.jdt.internal.junit5.runner.JUnit5TestLoader클래스를 의존관계로 가짐
    • JUnit5TestLoader클래스는 org.junit.platform.launcher.Launcher클래스를 의존관계로 가짐
  • junit-platform-launcher라이브러리내의 Launcher클래스 load

    • 우선순위 : classpath -> eclipse plugins내 최신 버전

따라서 STS4.6.0.RELEASE로 업데이트 된 후 junit-platform-launcher1.6.0을 사용하게 되면서 org.junit.platform.engine.EngineDiscoveryListener클래스를 찾게 된 것입니다.

여기에 의존성을 추가하여 classpathjunit-platform-launcher:1.5.1를 추가하여 해당 버전의 launcher를 사용할 수 있도록 한 것입니다.