Unit test is very essential in ensuring the quality of source code developed by developers. In the Java world, JUnit is a very simple framework that supports developers to implement Unit tests of their source code. The current stable version of JUnit is 4.12. However, with the goal is to support new features in Java 8 and above, as well as enabling many different styles of testing, the JUnit team is about to release the new version of JUnit, JUnit 5. In this post, I’d like to introduce about JUnit 5, its new features, and some examples.
1. What’s New in JUnit 5?
JUnit 5 is composed of several different modules from three different sub-projects.
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
1.1. JUnit Platform
- Be responsible for launching testing frameworks on the JVM. It defines a stable and powerful interface between JUnit and its clients such as build tools (Maven, Gradle, Ant, etc) and IDEs (Eclipse, IntelliJ, NetBeans, etc). The final objective is how its clients get integrated easily with JUnit in discovering and executing the tests.
- Defines the TestEngine API for developing a testing framework that runs on the Unit platform. So, 3rd testing libraries like Spock, Cucumber, and FitNesse can be plugged into JUnit platform by implementing a custom TestEngine.
- Provides a ConsoleLauncher to launch the platform from the command line and build plugins for Gradle, Maven, and JUnit 4
1.2. JUnit Jupiter
This module includes new programming model and extension model for writing tests and extensions in JUnit 5. We will see many new annotations like @BeforeEach, @AfterEach, @DisplayName, @Nested, @Tag, etc.
1.3. JUnit Vintage
Supports running JUnit 3 and JUnit 4 based tests on the platform.
2. Supported Java Versions
JUnit 5 requires Java 8. However, we can still test code that has been compiled with previous versions of the JDK.
3. JUnit 5 Annotations vs JUnit 4.x Annotation
JUnit 5 Annotations | Descriptions | JUnit 4 Annotations |
@Test | Declares a test method | @Test |
@TestFactory | Denotes a method is a test factory for dynamic tests in JUnit 5 | N/A |
@DisplayName | Define custom display name for a test class or test method | N/A |
@BeforeEach | Denotes that the annotated method will be executed before each test method(annotated with @Test) in the current class. | @Before |
@AfterEach | Denotes that the annotated method will be executed after each test method (annotated with @Test) in the current class. | @After |
@BeforeAll | Denotes that the annotated method will be executed before all test methods in the current class. | @BeforeClass |
@AfterAll | Denotes that the annotated method will be executed after all test methods in the current class. | @AfterClass |
@Nested | Denotes that the annotated class is a nested, non-static test class | N/A |
@Tag | Declare tags for filtering tests. | N/A |
@Disable | Is used to disable a test class or method. | @Ignore |
@ExtendWith | Is used to register custom extensions in JUnit 5 | N/A |
@RepeatedTest | Is used to repeat tests | N/A |
To compare JUnit 5 and JUnit 4, please visit JUnit 5 vs JUnit 4
4. JUnit 5 Basic Examples
4.1. Sample Source Code and Build file
You can get JUnit 5 basic examples with Maven and Gradle build files at this repository on Github.
4.2. JUnit 5 Basic Example with Maven
Here is an example pom.xml file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.howtoprogram</groupId> <artifactId>junit5-tutorial</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>junit5-tutorial</name> <url>https://howtoprogram.xyz</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <junit.jupiter.version>5.0.0</junit.jupiter.version> <junit.vintage.version>4.12.0</junit.vintage.version> <junit.platform.version>1.0.0</junit.platform.version> </properties> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.19</version> <dependencies> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-surefire-provider</artifactId> <version>${junit.platform.version}</version> </dependency> </dependencies> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-runner</artifactId> <version>${junit.platform.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit.jupiter.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> <version>${junit.vintage.version}</version> <scope>test</scope> </dependency> </dependencies> </project> |
To completely setup a JUnit 5 Maven project, please visit JUnit 5 Maven Example.
4.3. JUnit 5 Basic Example with Gradle
Here is an example build.gradle file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
buildscript { repositories { mavenCentral() } dependencies { classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0' } } repositories { mavenCentral() } ext.junitVintageVersion = '4.12.0' ext.junitPlatformVersion = '1.0.0' ext.junitJupiterVersion = '5.0.0' apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'idea' apply plugin: 'org.junit.platform.gradle.plugin' jar { baseName = 'junit5-tutorial' version = '1.0.0-SNAPSHOT' } compileTestJava { sourceCompatibility = 1.8 targetCompatibility = 1.8 options.compilerArgs += '-parameters' } dependencies { // JUnit Jupiter API and TestEngine implementation testCompile("org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}") testCompile("org.junit.platform:junit-platform-runner:${junitPlatformVersion}") testRuntime("org.junit.jupiter:junit-jupiter-engine:${junitJupiterVersion}") } |
To completely setup a JUnit 5 Gradle project, please visit JUnit 5 Gradle Example.
4.4. JUnit 5 Basic Example Tests
Following is a JUnit 5 example test class. Assume that we have a class: UserRepositoryImplFile that can be used to store user information in an XML file, and now we’re writing Unit tests for that class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
package com.howtoprogram.junit5; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @RunWith(JUnitPlatform.class) public class JUnit5UserRepositoryImplFileTest { @BeforeAll static void setup() { // Initialize connection to file. System.out.println("@BeforeAll - Execute once before all test methods in this class."); } @BeforeEach void init() { // Insert some sample data before each test System.out.println("@BeforeEach - Executed before each test method in this class."); } @DisplayName("Test add user successfully.") @Test void testAddUserSuccess() { System.out.println("Test add user successfully"); } @DisplayName("Test add user with passed argument is null.") @Test void testAddUserNull() { System.out.println("Test add null user."); } @Test @Disabled("Not implemented yet.") void testDeleteUser() {} @AfterEach void tearDown() { // Reset the file content. System.out.println("@AfterEach - This method is called after each test method."); } @AfterAll static void done() { // Closes connection to the file System.out.println("@AfterAll - This method is called after all test methods."); } } |
5. Run JUnit 5 Tests
5.1. Run JUnit 5 tests with Maven.
To run JUnit 5 tests with Maven, we simply need to go to project directory and issue below command:
1 |
mvn test |
The output for above test class is similar to below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
------------------------------------------------------- T E S T S ------------------------------------------------------- Running com.howtoprogram.junit5.JUnit5UserRepositoryImplFileTest @BeforeAll - Execute once before all test methods in this class. @BeforeEach - Executed before each test method in this class. Test add null user. @AfterEach - This method is called after each test method. @BeforeEach - Executed before each test method in this class. Test add user successfully @AfterEach - This method is called after each test method. @AfterAll - This method is called after all test methods. Tests run: 3, Failures: 0, Errors: 0, Skipped: 1, Time elapsed: 0.076 sec - in com.howtoprogram.junit5.JUnit5UserRepositoryImplFileTest Results : Tests run: 3, Failures: 0, Errors: 0, Skipped: 1 |
5.2. Run JUnit 5 test with Gradle
In similar to above, run JUnit 5 test with Gradle is very simple.
1 |
gradle test |
The output of above test class is similar to below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
:compileJava :processResources UP-TO-DATE :classes :compileTestJava :processTestResources UP-TO-DATE :testClasses :junitPlatformTest Aug 07, 2016 2:53:37 PM org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry loadTestEngines INFO: Discovered TestEngines with IDs: [junit-jupiter] @BeforeAll - Execute once before all test methods in this class. @BeforeEach - Executed before each test method in this class. Test add null user. @AfterEach - This method is called after each test method. @BeforeEach - Executed before each test method in this class. Test add user successfully @AfterEach - This method is called after each test method. @AfterAll - This method is called after all test methods. Test run finished after 465 ms [ 3 tests found ] [ 1 tests skipped ] [ 2 tests started ] [ 0 tests aborted ] [ 2 tests successful ] [ 0 tests failed ] [ 0 containers failed] |
5.3. Run JUnit 5 test with IntelliJ
IntelliJ supports JUnit 5 by default. Therefore, running JUnit 5 on IntelliJ is pretty simple, simply Right click –> Run, or Ctrl-Shift-F10
5.4. Run JUnit 5 test with Eclipse
Currently, there is no direct support to run Unit tests on the JUnit Platform within IDEs. This means that, in Eclipse, when you right click on the test class, select Run As on the menu, there will be no JUnit Test menu item for you to select. However, we can work around by running the JUnit 5 tests with a JUnit 4 based Runner. In short, we can annotate our class with the @RunWith(JUnitPlatform.class) annotation. Let’s take a look again at our above test class.
1 2 3 4 |
@RunWith(JUnitPlatform.class) public class JUnit5UserRepositoryImplFileTest { } |
Now the IDE will recognize the JUnit 5 test classes and allow us to run the tests directly in the IDE. In Eclipse, we just need to open the test class, right click on the class, under Run As menu, select JUnit Test menu item and the test class will be executed.
Here is the result in my Eclipse.
6. Conclusion
We have just taken a quick look at JUnit 5 which includes:
- Some new features of JUnit5
- Some new annotations of JUnit 5
- Some JUnit 5 basic examples with Maven and Gradle
- Run JUnit 5 tests with Maven, Gradle, Eclipse, and IntelliJ IDEA.
Hopefully, they’re enough for you to get started with JUnit 5. If you want more JUnit 5 tutorials, you can refer to my recent posts:
JUnit 5 and Spring Boot Example
JUnit 5 Disable or Ignore A Test
JUnit 5 Dynamic Tests – Generate Tests at Run-time
JUnit 5 Test Suite – Aggregating Tests In Suites
JUnit 5 Parameter Resolution Example