Friday, December 9, 2016

Playing with JUnit 5 and IntelliJ IDEA 2016.3

    I've been recently playing with JUnit 5 M3 and IntelliJ IDEA 2016.3. I managed to execute JUnit 5 tests from within IntelliJ IDEA and Maven. I decided to share it with you because it was not trivial.

IntelliJ IDEA 2016.3:
  • you need to add the following dependencies to you pom.xml file:
 <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.0.0-M3</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.0.0-M3</version>
        </dependency>
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-launcher</artifactId>
            <version>1.0.0-M3</version>
        </dependency>
</dependences>
Without org.junit.platform:junit-platform-launcher dependency you will receive the following exception:
org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry loadTestEngines
INFO: Discovered TestEngines with IDs: [junit-jupiter]
Exception in thread "main" java.lang.NoSuchMethodError:
org.junit.platform.commons.util.Preconditions.notNull([Ljava/lang/Object;Ljava/lang/String;)[Ljava/lang/Object;
    at org.junit.platform.launcher.core.DefaultLauncher.registerTestExecutionListeners(DefaultLauncher.java:71)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:44)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) 
This is due to the fact that IntelliJ 2016.3 JUnit 5 test runner is based on M2 version (it includes M2 dependencies unless you specify differently in your pom.xml) whereas we tried to use M3 version. The method:
    org.junit.platform.launcher.core.DefaultLauncher.registerTestExecutionListeners -> M2
tried to call:
public static  T notNull(T object, String message) throws PreconditionViolationException -> M3
// it lives in org.junit.platform:junit-platform-commons which is a transitive dependency of
// org.junit.jupiter:junit-jupiter-engine 
However, DefaultLauncher M2 was compiled against:
public static Object[] notNull(Object[] objects, String message) throws PreconditionViolationException -> M2
So despite the fact there is no compilation error - there is the lack of binary compatibility. The cure was to add org.junit.platform:junit-platform-launcher:1.0.0-M3 explicitly. Thanks to that DefaultLauncher M3 is used by IntelliJ 2016.3 JUnit 5 test runner and it calls Preconditions M3. You can also wait for IntelliJ IDEA 2016.3.1 or IntelliJ IDEA 2017.1 because the problem was fixed in these versions.

Maven:
  • you can use the same dependencies as for IntelliJ but you also need to add a plugin to your pom.xml:  
<plugin>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.19</version>
  <dependencies>
    <dependency>
      <groupId>org.junit.platform</groupId>
      <artifactId>junit-platform-surefire-provider</artifactId>
      <version>1.0.0-M3</version>
    </dependency>
  </dependencies>
</plugin>
P.S. This is the last post in 2016. Happy New Year and stay tuned for the upcoming entries in 2017!

1 comment :