Close

JUnit 5 - DynamicTest Factory Methods

[Last Updated: Dec 30, 2025]

While the basic dynamicTest(String, Executable) is useful for individual tests, JUnit Jupiter provides overloaded factory methods to generate multiple tests from data streams or iterators efficiently. These methods simplify the mapping between input data and test execution.

1. Custom Display Name Generation

This group allows you to provide raw data and separate functions to define how the test name is generated and how the test is executed.

public static <T> Stream<DynamicTest> stream(Iterator<T> inputGenerator,
                                             Function<? super T, String> displayNameGenerator,
                                             ThrowingConsumer<? super T> testExecutor)

public static <T> Stream<DynamicTest> stream(Stream<T> inputStream,
                                             Function<? super T,
                                             String> displayNameGenerator,
                                             ThrowingConsumer<? super T> testExecutor)
package com.logicbig.example;

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;

public class DynamicStreamTest {
    @TestFactory
    Stream<DynamicTest> streamExample() {
        return DynamicTest.stream(
            Stream.of("apple", "banana"),
            input -> "Testing length of: " + input,
            input -> assertTrue(input.length() > 0)
        );
    }
}

Output

$ mvn test -Dtest=DynamicStreamTest
[INFO] Scanning for projects...
[INFO]
[INFO] -----< com.logicbig.example:junit-5-dynamic-test-stream-generator >-----
[INFO] Building junit-5-dynamic-test-stream-generator 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ junit-5-dynamic-test-stream-generator ---
[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-test-stream-generator\src\main\resources
[INFO]
[INFO] --- compiler:3.13.0:compile (default-compile) @ junit-5-dynamic-test-stream-generator ---
[INFO] No sources to compile
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ junit-5-dynamic-test-stream-generator ---
[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-test-stream-generator\src\test\resources
[INFO]
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ junit-5-dynamic-test-stream-generator ---
[INFO] Recompiling the module because of changed source code.
[WARNING] File encoding has not been set, using platform encoding windows-1252, i.e. build is platform dependent!
[INFO] Compiling 4 source files with javac [debug target 17] to target\test-classes
[INFO]
[INFO] --- surefire:3.5.0:test (default-test) @ junit-5-dynamic-test-stream-generator ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] +--com.logicbig.example.DynamicStreamTest - 0.110 ss
[INFO] | +-- [OK] streamExample() Testing length of: apple - 0.019 ss
[INFO] | '-- [OK] streamExample() Testing length of: banana - 0.001 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.179 s
[INFO] Finished at: 2025-12-28T23:14:19+08:00
[INFO] ------------------------------------------------------------------------

2. Using Named Containers

By using the Named interface, you can bundle the payload and its display name into a single object, removing the need for a separate name generator function.

public static <T> Stream<DynamicTest> stream(Iterator<? extends Named<T>> inputGenerator,
                                                         ThrowingConsumer<? super T> testExecutor)

public static <T> Stream<DynamicTest> stream(Stream<? extends Named<T>> inputStream,
                                                         ThrowingConsumer<? super T> testExecutor)
package com.logicbig.example;

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

public class DynamicNamedTest {
    @TestFactory
    Stream<DynamicTest> namedStreamExample() {
        return DynamicTest.stream(
            Stream.of(Named.of("Null check", "data")),
            val -> assertNotNull(val)
        );
    }
}

Output

$ mvn test -Dtest=DynamicNamedTest
[INFO] Scanning for projects...
[INFO]
[INFO] -----< com.logicbig.example:junit-5-dynamic-test-stream-generator >-----
[INFO] Building junit-5-dynamic-test-stream-generator 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ junit-5-dynamic-test-stream-generator ---
[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-test-stream-generator\src\main\resources
[INFO]
[INFO] --- compiler:3.13.0:compile (default-compile) @ junit-5-dynamic-test-stream-generator ---
[INFO] No sources to compile
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ junit-5-dynamic-test-stream-generator ---
[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-test-stream-generator\src\test\resources
[INFO]
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ junit-5-dynamic-test-stream-generator ---
[INFO] Nothing to compile - all classes are up to date.
[INFO]
[INFO] --- surefire:3.5.0:test (default-test) @ junit-5-dynamic-test-stream-generator ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] +--com.logicbig.example.DynamicNamedTest - 0.146 ss
[INFO] | '-- [OK] namedStreamExample() Null check - 0.034 ss
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.498 s
[INFO] Finished at: 2025-12-28T23:14:25+08:00
[INFO] ------------------------------------------------------------------------

3. Named Executables

This group is the most concise when your stream already contains both the name and the logic (as an Executable) wrapped in a Named container.

public static <T extends Named<E>, E extends Executable> Stream<DynamicTest> stream(Iterator<? extends T> iterator)

public static <T extends Named<E>, E extends Executable> Stream<DynamicTest> stream(Stream<? extends T> inputStream)
package com.logicbig.example;

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

public class DynamicNamedExecutableTest {
    @TestFactory
    Stream<DynamicTest> namedExecutableExample() {
        return DynamicTest.stream(
            Stream.of(Named.of("Addition test", () -> assertEquals(4, 2 + 2)))
        );
    }
}

Output

$ mvn test -Dtest=DynamicNamedExecutableTest
[INFO] Scanning for projects...
[INFO]
[INFO] -----< com.logicbig.example:junit-5-dynamic-test-stream-generator >-----
[INFO] Building junit-5-dynamic-test-stream-generator 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ junit-5-dynamic-test-stream-generator ---
[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-test-stream-generator\src\main\resources
[INFO]
[INFO] --- compiler:3.13.0:compile (default-compile) @ junit-5-dynamic-test-stream-generator ---
[INFO] No sources to compile
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ junit-5-dynamic-test-stream-generator ---
[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-test-stream-generator\src\test\resources
[INFO]
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ junit-5-dynamic-test-stream-generator ---
[INFO] Nothing to compile - all classes are up to date.
[INFO]
[INFO] --- surefire:3.5.0:test (default-test) @ junit-5-dynamic-test-stream-generator ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] +--com.logicbig.example.DynamicNamedExecutableTest - 0.152 ss
[INFO] | '-- [OK] namedExecutableExample() Addition test - 0.026 ss
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.359 s
[INFO] Finished at: 2025-12-28T23:14:31+08:00
[INFO] ------------------------------------------------------------------------

4. Custom URI for Source Navigation

The URI parameter allows tools (like IDEs) to link the dynamic test to a specific source location (e.g., a line in a CSV file or a specific method), which is otherwise difficult for dynamically generated code.

public static DynamicTest dynamicTest(String displayName,
                                      @Nullable URI testSourceUri,
                                      Executable executable)
package com.logicbig.example;

import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import java.net.URI;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class DynamicUriTest {
    @TestFactory
    DynamicTest singleUriTest() {
        return DynamicTest.dynamicTest(
            "URI Source Test",
            URI.create("https://logicbig.com"),
            () -> assertTrue(true)
        );
    }
}

Output

$ mvn test -Dtest=DynamicUriTest
[INFO] Scanning for projects...
[INFO]
[INFO] -----< com.logicbig.example:junit-5-dynamic-test-stream-generator >-----
[INFO] Building junit-5-dynamic-test-stream-generator 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ junit-5-dynamic-test-stream-generator ---
[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-test-stream-generator\src\main\resources
[INFO]
[INFO] --- compiler:3.13.0:compile (default-compile) @ junit-5-dynamic-test-stream-generator ---
[INFO] No sources to compile
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ junit-5-dynamic-test-stream-generator ---
[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-test-stream-generator\src\test\resources
[INFO]
[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ junit-5-dynamic-test-stream-generator ---
[INFO] Nothing to compile - all classes are up to date.
[INFO]
[INFO] --- surefire:3.5.0:test (default-test) @ junit-5-dynamic-test-stream-generator ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] +--singleUriTest() - 0.095 ss
[INFO] | '-- [OK] URI Source Test - 0.016 ss
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.066 s
[INFO] Finished at: 2025-12-28T23:14:36+08:00
[INFO] ------------------------------------------------------------------------

Output Analysis

The output of the examples demonstrates how these factory methods reduce boilerplate. By using the specialized stream methods, we can transform raw data into a series of named tests in a single pipeline, while the URI-based method provides the necessary metadata for external reporting tools to track the source of dynamic failures.

Example Project

Dependencies and Technologies Used:

  • junit-jupiter-engine 6.0.1 (Module "junit-jupiter-engine" of JUnit)
     Version Compatibility: 5.11.0 - 6.0.1Version List
    ×

    Version compatibilities of junit-jupiter-engine with this example:

    • 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

JUnit 5 - DynamicTest Factory Methods Select All Download
  • junit-5-dynamic-test-stream-generator
    • src
      • test
        • java
          • com
            • logicbig
              • example
                • DynamicStreamTest.java

    See Also