Skip to content

Integrating JUnit 5

Estimated time to read: 6 minutes

Running tests from the console

The JUnit Console Launcher allows tests to be launched from the command line. It is an executable JAR that comes with all required dependencies to run JUnit test.

Dependency Information

  • Group ID: org.junit.platform
  • Artifact ID: junit-platform-console-standalone
  • Version: 1.x.x

Running Tests

The console launcher can be opened with the following command, java -jar junit-platform-console-standalone.jar.

It takes additional options such as:

  • -cp ${PATH-TO-COMPILED-CLASSES}, which is used for directing the JAR to compiled classes
  • --scan-classpath, which enables it to automatically look for methods with the test annotation

Argument Files

There can be system limitations with the maximum length of a command that can be run. The ConsoleLauncher supports argument files for long common lines.

Arguments can be entered into a file, separated by a new line or a space. Make sure to wrap arguments that include whitespace with single or double quotes.

args.txt
# Comment
-- cp build/classes/java
-- include-classname ".*Test.*"
-- scan-classpath --details flat

The arguments within the file can then be invoked with the @<filename> argument at the command line. For example, java -jar junit-platform-console-standalone.jar @args.txt

Running tests with Gradle

Gradle provides native support for executing JUnit 5 tests. Refer to 101 - Introducting JUnit 5 for a brief overview.

build.gradle
// Declare dependencies
dependencies {
 testImplementation(platform('org.junit:junit-bom:5.x.x'))
 testImplementation('org.junit.jupiter:junit-jupiter')
}

test {
 useJUnitPlatform() // (1)

 // Alternatively, this can be declared and other settings for the platform can be parsed in the build.gradle file
 useJUnitPlatform {
  includeTags 'v1', 'gift'
  excludeTags 'conversion'

  includeEngines 'junit-jupiter'
  excludeEngines 'junit-vintage'
 }

 filter { // filter can be used to include / exclude specified methods also
  includeTest 'io.entityfour.TestPackOne', 'testMethod1'
  excludeTest 'io.entityfour.TestPackOne', 'testMethod2'

  includeTestsMatching '*.TestPackOne*'
  excludeTestsMatching 'io.entitiyfour.*'
 }

 // Configuration parameters for JUnit can be provided as JVM System Properties
 systemProperty 'junit.jupiter.conditions.deactivate', 'io.entityfour.*'
 systemProperties = [
  'junit.jupiter.extensions.autodetection.enabled' : 'false',
  'junit.jupiter.testinstance.lifecycle.default' : 'per_method'
 ]
}

1.  Declare the JUnit platform usage

Running tests with Maven

Maven provides support for JUnit via the Maven Surefire Plugin for executing JUnit 5 tests. Refer to 101 - Introducting JUnit 5 for a brief overview.

By default, the Surefire plugin will automatically include all test classes with the following patterns:

  • **\Test\*.java
  • **\Test.java
  • **\Tests.java
  • **\TestCase.java

Further configuration can be done under the plugin configuration element within Maven's pom.xml file.

pom.xml
// ...

 <build>
  <plugins>
   <plugin>
    <artifiactId>maven-surefire-plugin</artifiactId>
    <version>2.22.2</version>
    <configuration>

     <includes> // Include none default files and packages
      <include>**/Test*.java</include>
      <include>**/package/*.*</include>
     </includes>

     <excludes> // Exclude files and packages
      <exclude>**/Test_Skip*.java</exclude>
     </excludes>

     <groups>v1, gift</groups> // Include specific tags
     <excludedGroups>conversion</excludedGroups> // Exclude specific tags

     <properties>
      <configurationParameters> // JVM System Properties can be defined within this element
       junit.jupiter.extensions.autodetection.enabled=false
       junit.jupiter.testinstance.lifecycle.default=per_method
      </configurationParameters>
     </properties>
    </configuration>
   </plugin>    
  </plugins>
 </build>

// ...

Include / Exclude tests with Tags

@tag are annotations that are used to tag classes or methods with a specific string value. More than one @tag can be applied to more than one element.

Classes / Methods

// ...
import org.junit.jupiter.api.Tag;
// ...

@Tag("v1")
@Tag("reward")

class TestRewards {
 @Test
 @Tag("fast")
 void testRewardProgram() {
  // ...
 }
}

Interfaces

@tag can also be used on interfaces. Classes then implement an annotated interface also inherit its semantics, thus inheriting the annotations.

ErrorHandler.java
@Tag('error')
@ExtendWith({ExceptionHandler.class})
public interface ErrorHandler {
 // ...
}
TestRewardProgram.java
// ...
class TestRewardProgram implements ErrorHandler {
 // ...
}

Meta-annotations

A @tag annotation can also meta-annotate another annotation, similar to @Test and @ExtendWith.

@Target({ElementType.Type, ElementType.Method})
@Retention(RetentionPolicy.RUNTIME)
@Test
@Tag("error")
@ExtendWith({ExceptionHandler.class})
public @interface TestWithErrorHandler() {
 // ...
}
@TestWithErrorHandler
void testRewardProgram() {
 // ...
}

@Tag Syntax Rules

The String value that the @Tag annotation receives must abide to the following:

  • A tag must not be null or blank
  • Trailing whitespace characters are automatically trimmed
  • A trimmed tag must not contain:
  • Whitespace
  • ISO Control Characters
  • Reserved Characters
    • , ( ) & | !

Tag Expressions

In both Gradle and Maven, tag expressions can be used:

Operator Description
! Not
& And
| Or
() To modify precedence
any() To select all tests with any tags
none() To select all tests without any tags

Example

v1 & !gift - Selects all v1 tests but ignores gift tests.

(gift | conversion) & (v1 | v2) - Selects all gift or conversion tests that are also tagged as v1 or v2

Code Coverage

Code Coverage is the measured used to describe the degree to which the code of your program is covered by your tests. How many lines of code are executed by tests? 🤔

This metric is collected by a tool that traces the calls made by the tests of the application and reports the percentage of the code that is executed.

JaCoCo

JaCoCo is a popular code coverage library for Java. Visit JaCoCo for more information.

Maven

Using JaCoCo with Maven requires the following setup:

  • JaCoCo added as a dependency to the pom.xml
  • Add the JaCoCo agent as a 'prepare-agent' execution goal
  • Create the report

The JaCoCo agent collects the coverage information, additionally the agent can be configured to generate a report from any phase of the build and at the report goal. Refer to Report below for more information.

pom.xml
// ...
 <plugin>
  <groupId>org.jacoco</groupId>
  <artifactId>jacoco-maven-plugin</artifactId>
  <version>x.y.z</version>

  <executions>
   <execution>
    <goals>
     <goal>prepare-agent</goal>
    </goals>
   </execution>

   <execution>
    <id>report</id>
    <phase>test</phase>
    <goals>
     <goal>report</goal>
    </goals>
   </execution>

  </executions>

 </plugin>

// ...

JaCoCo can also be configured to ensure that a specific percentage of code coverage has been undertaken. This can be done by adding the following to the pom.xml:

pom.xml
<execution>
 <id>jacoco-check</id>
 <phase>test</phase>
 <goals>
  <goal>check</goal>
 </goals>
 <configuration>
  <rules>
   <rule>
    <element>PACKAGE</element> // At the PACKAGE level,...
    <limits>
     <limit>
      <counter>LINE</counter> // ...Check against line...
      <value>COVEREDRATIO</value> // ...coverage ratio...
      <minimum>0.70</minimum> // ...of 70%. Else, fail the build.
     </limit>
    </limits>
   </rule>
  </rules>
 </configuration>
</execution>

Once configured in pom.xml. mvnw test can be run to execute the test phase. JaCoCo then outputs the following:

  • target/jacoco.exec - Contains information collected by the JoCoCo agent
  • target/site/jococo/* - Contains the generated report in CSV, HTML and XML formats

Gradle

Using JaCoCo with Maven requires the following setup:

  • JaCoCo added as a plugin to build.gradle
  • Create the report
  • Using finalizedBy jacocoTestReport
  • Using a jacocoTestReport task element
build.gradle
plugins {
 id 'java'
 id 'jacoco'
}

group 'io.entityfour'
version '1.0-SNAPSHOT'

repositories {
 mavenCentral()
}

dependencies {
 testImplementation(platform('org.junit:junit-bom:5.y.x'))
 testImplementation('org.junit.jupiter:junit-jupiter')
}

test {
 useJUnitPlatform()
 finalizedBy jacocoTestReport // The report will be generated after the tests are run.
}

// jacocoTestReport {
//  dependsOn test // Depends on the `test` task. Tests are required to run before generating the report.
// }

Once configured in build.gradle. gradlew test can be run to execute the test phase. JaCoCo then outputs the following:

  • build/jacoco/jacoco.exec - Contains information collected by the JoCoCo agent
  • build/reports/jacoco/test/html/* - Contains the generated report in HTML

Coverage Report

The report shows the percentage of code covered by tests.

Expanding a package within the report will show code coverage for each class within it.

Expanding a class will show code coverage for each method also with a line by line breakdown.

  • Green - Executed
  • Red - Not executed
  • Yellow - Branch not executed

Report Location

The output location of the report varies depending on if Gradle or Maven were used.

Under Gradle, the report is output to - build/reports/jacoco/test/html/* Under Maven, the report is output to - target/site/jococo/*

The generated report is the same regardless of the build tool used.