Dynamic tests in JUnit 5 allow test cases to be generated at runtime rather than being declared statically at compile time. This capability is particularly useful when test data or test structures are not known upfront.
Dynamic tests are created using methods annotated with @TestFactory. Such methods return a Collection, Iterable, Iterator, or Stream of DynamicTest class instances.
@TestFactory annotation
Definition of TestFactoryVersion: 6.0.0 package org.junit.jupiter.api;
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = MAINTAINED, since = "5.3")
@Testable
public @interface TestFactory {
}
DynamicTest class
The DynamicTest class is a specialized container in JUnit used to represent a test case generated at runtime.
Each DynamicTest instance consists of a display name (a String for reporting) and an executable (the actual test logic). These are essential when the number or structure of your tests depends on external factors like database results, file system contents, or complex code logic.
Instances of DynamicTest are not created using the new keyword. Instead, you use the static factory method:
DynamicTest.dynamicTest(String, Executable)
Why Dynamic Tests?
- Generate tests programmatically based on runtime data
- Avoid repetitive parameterized test declarations
- Create flexible and expressive test models
Dynamic Tests vs Static Tests
Unlike @Test or @ParameterizedTest, dynamic tests are not discovered at compile time and do not support per-test lifecycle callbacks.
Example
package com.logicbig.example;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class DynamicTestsIntroductionTest {
@TestFactory
List<DynamicTest> simpleDynamicTests() {
return Arrays.asList(
DynamicTest.dynamicTest("first test", () -> assertTrue(1 < 2)),
DynamicTest.dynamicTest("second test", () -> assertTrue(2 < 3))
);
}
}
Output$ mvn test -Dtest=DynamicTestsIntroductionTest [INFO] Scanning for projects... [INFO] [INFO] ------< com.logicbig.example:junit-5-dynamic-tests-introduction >------- [INFO] Building junit-5-dynamic-tests-introduction 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ junit-5-dynamic-tests-introduction --- [WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\junit-5\junit-5-dynamic-tests\junit-5-dynamic-tests-introduction\src\main\resources [INFO] [INFO] --- compiler:3.11.0:compile (default-compile) @ junit-5-dynamic-tests-introduction --- [INFO] No sources to compile [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ junit-5-dynamic-tests-introduction --- [WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory D:\example-projects\junit-5\junit-5-dynamic-tests\junit-5-dynamic-tests-introduction\src\test\resources [INFO] [INFO] --- compiler:3.11.0:testCompile (default-testCompile) @ junit-5-dynamic-tests-introduction --- [INFO] Changes detected - recompiling the module! :source [WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent! [INFO] Compiling 1 source file with javac [debug target 17] to target\test-classes [INFO] [INFO] --- surefire:3.5.0:test (default-test) @ junit-5-dynamic-tests-introduction --- [INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] +--com.logicbig.example.DynamicTestsIntroductionTest - 0.122 ss [INFO] | +-- [OK] simpleDynamicTests() first test - 0.015 ss [INFO] | '-- [OK] simpleDynamicTests() second test - 0.002 ss [INFO] [INFO] Results: [INFO] [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 4.392 s [INFO] Finished at: 2025-12-27T23:42:37+08:00 [INFO] ------------------------------------------------------------------------
Output Analysis
The output confirms that dynamic tests are created and executed at runtime, as each test is generated programmatically using @TestFactory and executed individually with its own display name.
Example ProjectDependencies and Technologies Used: - junit-jupiter-engine 6.0.1 (Module "junit-jupiter-engine" of JUnit)
Version Compatibility: 5.0.0 - 6.0.1 Version compatibilities of junit-jupiter-engine with this example:
- 5.0.0
- 5.0.1
- 5.0.2
- 5.0.3
- 5.1.0
- 5.1.1
- 5.2.0
- 5.3.0
- 5.3.1
- 5.3.2
- 5.4.0
- 5.4.1
- 5.4.2
- 5.5.0
- 5.5.1
- 5.5.2
- 5.6.0
- 5.6.1
- 5.6.2
- 5.6.3
- 5.7.0
- 5.7.1
- 5.7.2
- 5.8.0
- 5.8.1
- 5.8.2
- 5.9.0
- 5.9.1
- 5.9.2
- 5.9.3
- 5.10.0
- 5.10.1
- 5.10.2
- 5.10.3
- 5.10.4
- 5.10.5
- 5.11.0
- 5.11.1
- 5.11.2
- 5.11.3
- 5.11.4
- 5.12.0
- 5.12.1
- 5.12.2
- 5.13.0
- 5.13.1
- 5.13.2
- 5.13.3
- 5.13.4
- 5.14.0
- 5.14.1
- 6.0.0
- 6.0.1
Versions in green have been tested.
- JDK 25
- Maven 3.9.11
|