The NamedExecutable interface (introduced in 5.11) combines Executable and Named. This allows @TestFactory methods to return a stream of objects that carry both their execution logic and their display names, reducing the need for manual wrapping with DynamicTest.dynamicTest().
Java source and doc
Definition of NamedExecutableVersion: 6.0.0 package org.junit.jupiter.api;
@FunctionalInterface
@API(status = MAINTAINED, since = "5.13.3")
public interface NamedExecutable extends Named<Executable>, Executable {
@Override
default String getName() {
...
}
@Override
default Executable getPayload() {
...
}
}
Example
In this example, we use a Java record to implement NamedExecutable. This is a concise way to define data-driven scenarios where the record's state provides the test identity via getName() and the assertion logic via execute().
package com.logicbig.example;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.NamedExecutable;
import org.junit.jupiter.api.TestFactory;
import java.util.stream.Stream;
public class NamedExecutableTest {
@TestFactory
Stream<DynamicTest> dynamicTests() {
return DynamicTest.stream(Stream.of(
new MathTask(5, 5, 10),
new MathTask(20, 30, 50),
new MathTask(100, 1, 101)
));
}
record MathTask(int n1, int n2, int expected) implements NamedExecutable {
@Override
public void execute() {
System.out.println("Checking " + n1 + " + " + n2);
Assertions.assertEquals(expected, n1 + n2);
}
@Override
public String getName() {
return "Sum Test: " + n1 + " + " + n2 + " = " + expected;
}
}
}
Output$ mvn test -Dtest=NamedExecutableTest [INFO] Scanning for projects... [INFO] [INFO] -----------< com.logicbig.example:junit-5-named-executable >------------ [INFO] Building junit-5-named-executable 1.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- resources:3.3.1:resources (default-resources) @ junit-5-named-executable --- [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\dynamic-tests\junit-5-named-executable\src\main\resources [INFO] [INFO] --- compiler:3.13.0:compile (default-compile) @ junit-5-named-executable --- [INFO] No sources to compile [INFO] [INFO] --- resources:3.3.1:testResources (default-testResources) @ junit-5-named-executable --- [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\dynamic-tests\junit-5-named-executable\src\test\resources [INFO] [INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ junit-5-named-executable --- [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 1 source file with javac [debug target 17] to target\test-classes [INFO] [INFO] --- surefire:3.5.0:test (default-test) @ junit-5-named-executable --- [INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider [INFO] [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] [INFO] Results: [INFO] [INFO] Tests run: 0, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 3.948 s [INFO] Finished at: 2025-12-29T00:58:49+08:00 [INFO] ------------------------------------------------------------------------
(1) [WARNING] @TestFactory method 'java.util.stream.Stream<org.junit.jupiter.api.NamedExecutable> com.logicbig.example.NamedExecutableTest.dynamicTests()' must return a single org.junit.jupiter.api.DynamicNode or a Stream, Collection, Iterable, Iterator, Iterator provider, or array of org.junit.jupiter.api.DynamicNode. It will not be executed. Source: MethodSource [className = 'com.logicbig.example.NamedExecutableTest', methodName = 'dynamicTests', methodParameterTypes = ''] at com.logicbig.example.NamedExecutableTest.dynamicTests(SourceFile:0)
Output Analysis
The above output confirms that the JUnit 5 engine successfully discovered the NamedExecutable instances within the stream. Each test was executed with the custom name provided by the record's getName() method, demonstrating how this interface simplifies the creation of dynamic tests by bundling data, naming, and logic into a single cohesive unit.
Example ProjectDependencies and Technologies Used: - junit-jupiter-engine 6.0.1 (Module "junit-jupiter-engine" of JUnit)
Version Compatibility: 5.11.0 - 6.0.1 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
|
|