JUnit Platform Launcher provides APIs for discovering and executing tests on the JUnit Platform, typically used by IDEs and build tools
—
Comprehensive test plan representation with hierarchical test structure support, metadata access, and test identification capabilities.
Describes the tree of tests and containers discovered by a Launcher. Provides navigation and analysis capabilities for the discovered test structure.
/**
* Describes the tree of tests and containers discovered by a Launcher
*/
class TestPlan {
/**
* Get the root test identifiers in this plan
* @return Set of TestIdentifier instances representing roots
*/
Set<TestIdentifier> getRoots();
/**
* Get the parent of a given test identifier
* @param child - TestIdentifier to find parent for
* @return Optional containing parent TestIdentifier, empty if no parent
*/
Optional<TestIdentifier> getParent(TestIdentifier child);
/**
* Get direct children of a given test identifier
* @param parent - TestIdentifier to get children for
* @return Set of direct child TestIdentifier instances
*/
Set<TestIdentifier> getChildren(TestIdentifier parent);
/**
* Get direct children of a test identifier by string ID
* @param parentId - String ID of parent to get children for
* @return Set of direct child TestIdentifier instances
*/
Set<TestIdentifier> getChildren(String parentId);
/**
* Get direct children of a test identifier by UniqueId
* @param parentId - UniqueId of parent to get children for
* @return Set of direct child TestIdentifier instances
*/
Set<TestIdentifier> getChildren(UniqueId parentId);
/**
* Get all descendants of a given test identifier
* @param parent - TestIdentifier to get descendants for
* @return Set of all descendant TestIdentifier instances
*/
Set<TestIdentifier> getDescendants(TestIdentifier parent);
/**
* Get test identifier by unique ID string
* @param uniqueId - String representation of unique ID
* @return TestIdentifier with the specified unique ID
* @throws PreconditionViolationException if not found
*/
TestIdentifier getTestIdentifier(String uniqueId);
/**
* Get test identifier by unique ID
* @param uniqueId - UniqueId to look up
* @return TestIdentifier with the specified unique ID
* @throws PreconditionViolationException if not found
*/
TestIdentifier getTestIdentifier(UniqueId uniqueId);
/**
* Count test identifiers matching a predicate
* @param predicate - Predicate to match test identifiers against
* @return Number of matching test identifiers
*/
long countTestIdentifiers(Predicate<? super TestIdentifier> predicate);
/**
* Check if this plan contains any tests (not just containers)
* @return true if plan contains at least one test, false otherwise
*/
boolean containsTests();
/**
* Get configuration parameters for this test plan
* @return ConfigurationParameters instance
*/
ConfigurationParameters getConfigurationParameters();
/**
* Get output directory provider for this test plan
* @return OutputDirectoryProvider instance
*/
OutputDirectoryProvider getOutputDirectoryProvider();
/**
* Accept a visitor for traversing the test plan
* @param visitor - Visitor instance to traverse the plan
*/
void accept(Visitor visitor);
}Usage Examples:
import org.junit.platform.launcher.*;
// Analyze test plan structure
TestPlan testPlan = launcher.discover(request);
// Get basic statistics
long totalTests = testPlan.countTestIdentifiers(TestIdentifier::isTest);
long totalContainers = testPlan.countTestIdentifiers(TestIdentifier::isContainer);
System.out.println("Found " + totalTests + " tests in " + totalContainers + " containers");
// Navigate the hierarchy
for (TestIdentifier root : testPlan.getRoots()) {
printTestHierarchy(testPlan, root, 0);
}
private void printTestHierarchy(TestPlan testPlan, TestIdentifier identifier, int depth) {
String indent = " ".repeat(depth);
String type = identifier.isTest() ? "TEST" : "CONTAINER";
System.out.println(indent + type + ": " + identifier.getDisplayName());
for (TestIdentifier child : testPlan.getChildren(identifier)) {
printTestHierarchy(testPlan, child, depth + 1);
}
}
// Find tests with specific tags
Set<TestIdentifier> taggedTests = testPlan.getRoots().stream()
.flatMap(root -> testPlan.getDescendants(root).stream())
.filter(TestIdentifier::isTest)
.filter(test -> test.getTags().stream()
.anyMatch(tag -> tag.getName().equals("integration")))
.collect(Collectors.toSet());Immutable data transfer object representing a test or container. Provides access to metadata and hierarchical information.
/**
* Immutable data transfer object representing a test or container
*/
class TestIdentifier implements Serializable {
/**
* Get unique ID as string
* @return String representation of unique ID
*/
String getUniqueId();
/**
* Get unique ID object
* @return UniqueId instance
*/
UniqueId getUniqueIdObject();
/**
* Get parent ID as string
* @return Optional containing parent ID string, empty if no parent
*/
Optional<String> getParentId();
/**
* Get parent ID object
* @return Optional containing parent UniqueId, empty if no parent
*/
Optional<UniqueId> getParentIdObject();
/**
* Get display name for this test or container
* @return Human-readable display name
*/
String getDisplayName();
/**
* Get legacy reporting name for backwards compatibility
* @return Legacy reporting name
*/
String getLegacyReportingName();
/**
* Get descriptor type
* @return Type enum (TEST or CONTAINER)
*/
Type getType();
/**
* Check if this identifier represents a test
* @return true if this is a test, false if container
*/
boolean isTest();
/**
* Check if this identifier represents a container
* @return true if this is a container, false if test
*/
boolean isContainer();
/**
* Get test source information
* @return Optional containing TestSource, empty if no source
*/
Optional<TestSource> getSource();
/**
* Get associated tags
* @return Set of TestTag instances
*/
Set<TestTag> getTags();
}Usage Examples:
// Analyze test identifier
TestIdentifier testId = // ... obtained from test plan
System.out.println("Test: " + testId.getDisplayName());
System.out.println("Unique ID: " + testId.getUniqueId());
System.out.println("Type: " + (testId.isTest() ? "TEST" : "CONTAINER"));
// Check for specific tags
boolean isIntegrationTest = testId.getTags().stream()
.anyMatch(tag -> tag.getName().equals("integration"));
// Get source information
testId.getSource().ifPresent(source -> {
if (source instanceof ClassSource) {
ClassSource classSource = (ClassSource) source;
System.out.println("Class: " + classSource.getClassName());
} else if (source instanceof MethodSource) {
MethodSource methodSource = (MethodSource) source;
System.out.println("Method: " + methodSource.getClassName() + "#" + methodSource.getMethodName());
}
});
// Check parent relationship
testId.getParentId().ifPresent(parentId ->
System.out.println("Parent: " + parentId));/**
* Enum representing the type of a TestIdentifier
*/
enum Type {
/** Represents a container that can contain other tests or containers */
CONTAINER,
/** Represents an individual test case */
TEST
}The TestPlan supports the Visitor pattern for traversing the test hierarchy:
/**
* Visitor interface for traversing test plans
*/
interface Visitor {
/**
* Called before visiting a container's children
* @param testIdentifier - The container TestIdentifier being visited
*/
default void preVisitContainer(TestIdentifier testIdentifier) {}
/**
* Visit a test identifier (test or container)
* @param testIdentifier - The TestIdentifier being visited
*/
void visit(TestIdentifier testIdentifier);
/**
* Called after visiting a container's children
* @param testIdentifier - The container TestIdentifier being visited
*/
default void postVisitContainer(TestIdentifier testIdentifier) {}
}Usage Example:
// Custom visitor to collect all test methods
class TestMethodCollector implements TestPlan.Visitor {
private final List<TestIdentifier> testMethods = new ArrayList<>();
@Override
public void visit(TestIdentifier testIdentifier) {
if (testIdentifier.isTest()) {
testMethods.add(testIdentifier);
}
}
@Override
public void preVisitContainer(TestIdentifier testIdentifier) {
System.out.println("Entering container: " + testIdentifier.getDisplayName());
}
@Override
public void postVisitContainer(TestIdentifier testIdentifier) {
System.out.println("Exiting container: " + testIdentifier.getDisplayName());
}
public List<TestIdentifier> getTestMethods() {
return Collections.unmodifiableList(testMethods);
}
}
// Use visitor
TestMethodCollector collector = new TestMethodCollector();
testPlan.accept(collector);
List<TestIdentifier> allTests = collector.getTestMethods();TestPlan provides access to configuration and output management:
/**
* Configuration parameters for test execution
*/
interface ConfigurationParameters {
Optional<String> get(String key);
boolean getBoolean(String key);
int getInt(String key);
// Additional parameter access methods
}
/**
* Provider for output directories during test execution
*/
interface OutputDirectoryProvider {
Path createOutputDirectory(TestIdentifier testIdentifier);
Path createTempDirectory(TestIdentifier testIdentifier, String prefix);
}TestIdentifier implements Serializable, enabling:
Note that TestPlan itself is not serializable, but individual TestIdentifier instances can be serialized and later used to look up information in a new TestPlan instance.
Install with Tessl CLI
npx tessl i tessl/maven-org-junit-platform--junit-platform-launcher