JUnit 5 Dynamic Tests to Create Flexible and Data-Driven Tests
Last Updated :
28 Oct, 2024
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 Structure
After the project creation done successfully, set the folder structure like below:
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:
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:
This indicates that all the dynamic tests across the different classes have passed successfully.
In IntelliJ Click run button to run the test cases:
Similar Reads
Difference Between Unit Tests and Functional Tests
The article focuses on discussing the differences between Unit Testing and Functional Testing. The following topics will be discussed here: What is Unit Testing?What is Functional Testing?Unit Testing vs Functional Testing Let's start discussing each of these topics in detail. What is Unit Testing?
3 min read
How to Create Test Suite in TestNG?
In this article we will learn about test suites and how can we build them using Java and TestNG. TestNG is a modern testing framework that is used very widely used today. It has a broad base of features that allow us to write unit tests and club them together in groups. Table of Content What is a Te
11 min read
How to disable a TestNG test based on a condition?
TestNG stands for the Test Next Generation, it is an open-source and automated framework that is used for testing, it is used for the testing of codes in Java programming language, Cedric Beust.TestNG includes lots of test categories such as integration, functional and unit testing, etc. and it allo
3 min read
Encode and Decode Strings in Java with JUnit Tests
Strings are very useful and they can contain sequences of characters and there are a lot of methods associated with Strings. Moreover, encoding and decoding are possible for a given use case and it can be validated whether are they equal or not for a given requirement. They can be tested for validit
4 min read
How to create nested test suites for Selenium IDE?
When automating browser tests using Selenium IDE, it's important to structure your tests efficiently. A common approach for handling complex test scenarios is to create nested test suites. Test suites allow you to organize multiple tests and execute them together, while nested test suites give you e
2 min read
How to retrieve all test methods name in TestNG suite?
TestNG is a powerful testing framework that provides easy configuration and management of test cases. One useful feature of TestNG is the ability to retrieve and display all test methods in a test suite. In this example, we demonstrate how to access and print the names of all test methods defined in
4 min read
Use of data files (CSV, JSON) in Postman for data-driven testing
It is a software testing method having all test input data stored or documented in a single CSV/JSON file and using a single test script for testing the API request with various input scenarios. It separates the test script logic from the test data. In Postman, we can perform this using variables an
2 min read
What are Exclude and Include Test Methods in TestNG?
TestNG is a popular Java testing framework that stands for Test-Next Generation. It is an advanced form of JUnit, and offers comprehensive features, especially when it comes to integration tests. In this article, we will learn how to include and exclude test cases from executing at the Method, Class
13 min read
How to Rerun the Failures in a BDD Junit Test Automation Framework?
We come across failures many times after we execute our test scenarios through automation. These failures can be because of majorly two reasons: Functional Issue (actual failure).Intermittent Issues (Application Slowness, Page not loaded properly, Technical Issue, etc.). Having a mechanism to automa
7 min read
Difference Between junit-vintage-engine and junit-jupiter-engine
JUnit 5 introduced a modular architecture for testing in Java, allowing developers to run tests from multiple versions of JUnit and adopt new testing paradigms. Two of the key components of JUnit are the junit-vintage-engine and the junit-jupiter-engine. Each serves a different purpose, one focuses
6 min read