Open In App

JUnit 5 Dynamic Tests to Create Flexible and Data-Driven Tests

Last Updated : 28 Oct, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

JUnit 5 introduces the powerful feature called Dynamic Tests, which allows for creating test cases at runtime. Unlike static tests that are defined at the compile time, dynamic tests are generated during the execution, offering flexibility in scenarios where the number of the tests or their data is not known. This feature makes it possible to run the parameterized tests or loop over the test cases dynamically.

In this article, we will learn the dynamic tests in JUnit 5, how to write them, and the various examples that demonstrate their usage.

Prerequisites:

  • Basic knowledge of the Java and JUnit Framework.
  • JUnit 5 Set up in your project.
  • Maven for building dependency management.
  • JDK and IntelliJ IDEA installed in your system.

To include JUnit 5 in the Maven project, add the following dependencies:

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.1</version>
<scope>test</scope>
</dependency>

Dynamic Tests

Dynamic tests in JUnit 5 are created at the runtime and are useful in the cases where test cases are data-driven or dependent on the some runtime conditions. Unlike static tests (annotated with @Test), dynamic tests are created using the @TestFactory annotation.

Key Characteristics:

  • They are produced by the factory methods annotated with @TestFactory.
  • They return the Stream, Collection, or Iterable of DynamicTest instances.
  • Each dynamic test must have the display name and executable test code.

Writing Dynamic Tests

Dynamic tests are written using the factory method, and they can be defined using:

  • Collections
  • Streams
  • Iterators
  • Dynamic Containers (grouping multiple dynamic tests)

Example 1: Simple Dynamic Test with a Collection

import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.api.function.Executable;

import java.util.Arrays;
import java.util.Collection;

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

class DynamicTestExample {

@TestFactory
Collection<DynamicTest> dynamicTestsWithCollection() {
return Arrays.asList(
DynamicTest.dynamicTest("Test 1 + 1 = 2", () -> assertEquals(2, 1 + 1)),
DynamicTest.dynamicTest("Test 2 * 2 = 4", () -> assertEquals(4, 2 * 2))
);
}
}

In this example, two dynamic tests are generated and executed:

  • The first test checks if 1 + 1 equals 2.
  • The second test checks if 2 * 2 equals 4.

Example 2: Dynamic Tests with Streams

Streams are the powerful way to create the dynamic tests when the number of the test cases depends on the runtime conditions.

import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertTrue;

class DynamicTestStreamExample {

@TestFactory
Stream<DynamicTest> dynamicTestsWithStream() {
return Stream.of("apple", "banana", "cherry")
.map(fruit -> DynamicTest.dynamicTest("Check if " + fruit + " contains 'a'",
() -> assertTrue(fruit.contains("a"))));
}
}

In this example, dynamic tests are created for the each fruit in the stream. Each test checks if the fruit name contains the letter "a".

Example 3: Dynamic Tests with Iterators

We can also create the dynamic tests using iterators.

import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import java.util.Iterator;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertNotNull;

class DynamicTestIteratorExample {

@TestFactory
Iterator<DynamicTest> dynamicTestsWithIterator() {
List<String> inputData = List.of("A", "B", "C", "D");
return inputData.iterator();
}
}

Dynamic Containers

The Dynamic Container allows for grouping related dynamic tests. It can be useful when dealing with hierarchical test structures.

Example 4: Dynamic Test Containers

import org.junit.jupiter.api.DynamicContainer;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertTrue;

class DynamicTestContainerExample {

@TestFactory
Stream<DynamicContainer> dynamicTestsWithContainers() {
return Stream.of("Fruit tests", "Animal tests")
.map(group -> DynamicContainer.dynamicContainer(group, Stream.of(
DynamicTest.dynamicTest("Check 1 contains 1", () -> assertTrue(1 == 1)),
DynamicTest.dynamicTest("Check 2 contains 2", () -> assertTrue(2 == 2))
)));
}
}

In this example, two containers can be group related dynamic tests:

  • Fruit tests container
  • Animal tests container

Each container has its own set of the tests, providing better organization.

Project to Implement Dynamic Tests in Junit 5

In this project, we will create the simple Maven-based Java project to demonstrates the dynamic tests in the JUnit 5. The project includes various types of the dynamic tests such as:

  • Basic dynamic tests using collections
  • Dynamic tests using streams
  • Dynamic containers for grouping related tests

Step 1: Create a Maven Project

Create a new Maven Project using the IntelliJ IDEA. Choose the following options:

  • Name: dynamic-tests-junit5
  • Build System: Maven

Click on the Create button.

Project Metadata

Project Structure

After the project creation done successfully, set the folder structure like below:

Project Folder Structure

Step 2: Add Junit Dependencies to pom.xml

Open the pom.xml file and add the following dependencies into the Maven project.

XML
<?xml version="1.0" encoding="UTF-8"?>
<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.gfg</groupId>
    <artifactId>dynamic-tests-junit5</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- JUnit 5 Dependencies -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.7.1</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.7.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
                <configuration>
                    <!-- Print out test result details -->
                    <printSummary>true</printSummary>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Step 3: Main Class

Java
package com.gfg;

public class Main {
    public static void main(String[] args) {
        System.out.println("Dynamic Tests with JUnit 5");
    }
}

Step 4: DynamicTestMain Class

Create the DynamicTestMain class and this class can be done by running the tests from different test classes like DynamicTestExample, DynamicTestStreamExample, and DynamicTestContainerExample.

DynamicTestMain.java:

Java
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.api.DynamicContainer;

import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Stream;

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

public class DynamicTestMain {

    // Dynamic test using collection
    @TestFactory
    Collection<DynamicTest> dynamicTestsWithCollection() {
        return Arrays.asList(
                DynamicTest.dynamicTest("Test 1 + 1 = 2", () -> assertEquals(2, 1 + 1)),
                DynamicTest.dynamicTest("Test 2 * 2 = 4", () -> assertEquals(4, 2 * 2)),
                DynamicTest.dynamicTest("Test 3 - 1 = 2", () -> assertEquals(2, 3 - 1))
        );
    }

    // Dynamic test using stream
    @TestFactory
    Stream<DynamicTest> dynamicTestsWithStream() {
        return Stream.of("apple", "banana", "cherry")
               .map(fruit -> DynamicTest.dynamicTest("Check if " + fruit + " contains 'a'",
                        () -> assertTrue(fruit.contains("a"))));
   }

    // Dynamic test using containers to group tests
    @TestFactory
    Stream<DynamicContainer> dynamicTestsWithContainers() {
        return Stream.of("Group 1", "Group 2")
                .map(group -> DynamicContainer.dynamicContainer(group, Stream.of(
                        DynamicTest.dynamicTest("Check if 1 equals 1", () -> assertTrue(1 == 1)),
                        DynamicTest.dynamicTest("Check if 2 equals 2", () -> assertTrue(2 == 2))
                )));
    }
}

Step 5: Run the Application

Once the project is completed, it will display the below output:

Application Running

Step 6: Running the Tests

Once we have written these test classes, we can run them using Maven.

To run the tests using Maven, execute the following command in the project directory:

mvn test

Output:

Tests Running

This indicates that all the dynamic tests across the different classes have passed successfully.

In IntelliJ Click run button to run the test cases:

Tests Running in IDE


Next Article
Article Tags :

Similar Reads