Junit 5 Jupiter

Junit 5 logoWhenever we write code, we have to test it. Junit is the primary candidate for writing test cases. the latest version of Junit is Junit 5 Jupiter.

JUnit is a simple framework for writing tests of Java code using Java.

Junit 5 has adapted the java 8 coding style. which means all the latest java 8 features like lambda, streams, etc can be used while writing test cases in JUnit 5. JUnit 5 also maintained backward compatibility, which means JUnit 4 or JUnit 3 test cases or a mixture of legacy versions of test cases along with JUnit 5 can also be executed on the JUnit 5 execution engine platform.

JUnit 4 required at least JDK 5 but Junit 5 will work with JDK 8 or above.

the latest version of JUnit 5 can be downloaded from https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api

Junit 5 Architecture

JUnit 4 was a monolithic project, it bundled all the functionality in 1 artifact, except Hamcrest it had no dependency. APIs required by the developer to write test cases and Api's required by build tools like Gradle, maven, ant, or development tools like IntelliJ or Eclipse, all were bundled into 1 artifact. It was very hard to maintain and was a violation of the Single Responsibility Principle.

To address this issue Junit team redesign the architecture and split the JUnit 5 project into 3 different modules.

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

junit-jupiter-api

JUnit Jupiter API is used for writing tests and extensions. it contains all the annotations and assertions offered by Junit 5 framework.

junit-jupiter-engine

JUnit Jupiter test engine will provide and run time engine to the executed test case, it's only required at runtime.

junit-vintage-engine

if you have a mixed JUnit 4 or JUnit 3 test case along with the latest JUnit 5 test cases, then the application needs backward compatibility, JUnit Vintage engine provides the platform to run JUnit 3 or JUnit 4 test case on the new JUnit 5 Platform.

JUnit 5 and JUnit Jupiter are not synonymously

It's important to understand that Junit 5 and Junit Jupiter are not the same. JUnit 5 is a project and version and Junit Jupiter is 1 of the modules of the JUnit 5 project which contains all the API and annotations required to write test cases in JUnit 5 projects.

Junit Jupiter Api Life Cycle

With each release, artifacts increment version no in the form of <major>.<minor>.<patch>, add some new API, increase the stability of existing API and deprecate or remove some old, but the Junit team wanted to make it more robust. In order to make it more prominent, Junit Team used @API Guardian project and its org.apiguardian.api.API.Status API life cycles enum and decorates every interface class and method with it.

  • INTERNAL

Api decorated with INTERNAL status must be used with JUnit only and can be removed as per requirement without prior notice.

  • DEPRECATED

Api decorated with DEPRECATED status should be avoided because they will be removed in subsequent releases.

  • EXPERIMENTAL

Api decorated with EXPERIMENTAL status are experimental features and relatively new they are in the beta testing phase if no major drawback or bug fount then they can be promoted to MAINTAINED, STABLE or DEPRECATED as well.

  • MAINTAINED

Api decorated with MAINTAINED status is for supporting the current stable API.

  • STABLE

Api's decorated with STABLE status will not be changed in a future release which might break the existing functionality.

so which dependency do I need?

if you are working with JUnit r5.3.2 (release 5.3.2) or lower than that.

dependencies {
    testCompile('org.junit.jupiter:junit-jupiter-api:5.3.2')
    testCompile('org.junit.jupiter:junit-jupiter-params:5.3.2')
    testRuntime('org.junit.jupiter:junit-jupiter-engine:5.3.2')
}

if you are using JUnit 5.4.0-M1 (milestone:5.4.0) or above then

testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')

JUnit team has aggregated all the required 3 dependencies into 1, just use JUnit-Jupiter it has 3 transitive dependencies.

  • junit-jupiter-api (a compile dependency)
  • junit-jupiter-params (a compile dependency)
  • junit-jupiter-engine (a runtime dependency)
package org.wesome.junit5;

public class AppleCalculator {
    public int addApple(int apple1, int apple2) {
        return apple1 + apple2;
    }
}
package org.wesome.junit5;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

class AppleCalculatorTest {

    @Test
    void addApple() {
        AppleCalculator appleCalculator = new AppleCalculator();
        assertEquals(2, appleCalculator.addApple(1, 1), "1 apple + 1 apple is 2 apple");
    }
}
plugins {
    id 'java'
}

group = 'org.wesome'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_1_8

repositories {
    mavenCentral()
}
dependencies {
    testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
}

test {
    useJUnitPlatform()
}

Junit 5 Gradle Plugin

The Junit team used to provide a junit-platform-gradle-plugin, but it was deprecated in JUnit Platform 1.2 and discontinued in 1.3.

Now Gradle provides standard test task native support for executing tests on the JUnit Platform read more.

plugins {
    id 'java'
}

group = 'org.wesome'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_1_8

repositories {
    mavenCentral()
}
dependencies {
    testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
}

test {
    useJUnitPlatform()
}

test task will generate an HTML report in {project}\build\reports\tests\test\index.html file.

Gradle Running Test Case Status

The latest Gradle build tool has reduced most of the standard output and gives a clean final status X actionable tasks: X executed.
this looks good when the only confirmation is required that all the test cases have run properly. but at the time of development, more insight of test case status will be beneficial.
we can force Gradle to show the final status of test cases.

plugins {
    id 'java'
}

group = 'org.wesome'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_1_8

repositories {
    mavenCentral()
}
dependencies {
    testImplementation('org.junit.jupiter:junit-jupiter:5.6.2')
}

test {
    testLogging {
        events "started", "passed", "skipped", "failed"
    }
    useJUnitPlatform()
}

Migrating from JUnit 4

Most of the features provided in JUnit 4 are also provided in JUnit 5 as well. so there will not be much migration and rewriting on JUnit 4 test cases code.

JUnit 5 provides an easy code migration using JUnit Vintage test engine. The vintage test engine allows JUnit 3 and JUnit 4 to run on the JUnit Platform infrastructure. All the classes and annotations of  JUnit 4 and JUnit 5 Jupiter are placed in org.junit.jupiter package.

  • All Junit 5 Jupiter Annotations are in  org.junit.jupiter.api package and Assertions are in org.junit.jupiter.api.Assertions package.
  • Junit 4 @Before and @After annotations have been replated with @BeforeEach and @AfterEach annotations.

  • Junit 4 @BeforeClass and @AfterClass annotations have been replated with @BeforeAll and @AfterAll annotations.

  • Junit 4 @Ignore annotation has been replated with @Disabled annotation.

  • Junit 4 @Categoryannotations have been replated with @Tag annotation.

  • Junit 4 @RunWith annotations have been replaced with @ExtendWith annotation.

  • Junit 4 @Rule and @ClassRule annotations have been replated with @ExtendWith and @RegisterExtension annotation.

Open Test Alliance

We have multiple testing frameworks like JUnit, TestNG, Mokito, Spock, etc, multiple third-party assertion libraries like Hamcrest, AssertJ, etc we have multiple build tools like Gradle, Maven Ent, etc, and development tools as well as IntelliJ, Eclipse Netbeans. Every Build tool and development tool needs to support all testing frameworks. There should be some kind of facade or standard API which everyone follows so that every Build tool and development tool doesn't have to implement framework-specific support.

Currently, most of the testing framework and assertions framework doesn't follow a predefined common set of rules, instead, most of them have implemented their own variations. Some testing frameworks express test case failure as AssertionError and some as RuntimeException which makes it hard for build tools development tools to support.

to resolve this issue JUnit team has proposed an open standard called Open Test Alliance for the JVM for testing libraries on the JVM.

Proposal

Based on discussions with IDE and build tool developers from Eclipse, Gradle, and IntelliJ, the JUnit 5 team is working on a proposal for an open-source project to provide a minimal common foundation for testing libraries on the JVM. The primary goal of the project is to enable testing frameworks like JUnit, TestNG, Spock, etc. and third-party assertion libraries like Hamcrest, AssertJ, etc. to use a common set of exceptions that IDEs and build tools can support in a consistent manner across all testing scenarios -- for example, for consistent handling of failed assertions and failed assumptions as well as visualization of test execution in IDEs and reports.

follow us on