org.junit.jupiter.api.DynamicTest Maven / Gradle / Ivy
Show all versions of junit-jupiter-api Show documentation
/*
* Copyright 2015-2024 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
* accompanies this distribution and is available at
*
* https://www.eclipse.org/legal/epl-v20.html
*/
package org.junit.jupiter.api;
import static java.util.Spliterator.ORDERED;
import static java.util.Spliterators.spliteratorUnknownSize;
import static org.apiguardian.api.API.Status.MAINTAINED;
import java.net.URI;
import java.util.Iterator;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.apiguardian.api.API;
import org.junit.jupiter.api.function.Executable;
import org.junit.jupiter.api.function.ThrowingConsumer;
import org.junit.platform.commons.util.Preconditions;
/**
* A {@code DynamicTest} is a test case generated at runtime.
*
* It is composed of a {@linkplain DynamicNode#getDisplayName display name}
* and an {@link #getExecutable Executable}.
*
*
Instances of {@code DynamicTest} must be generated by factory methods
* annotated with {@link TestFactory @TestFactory}.
*
*
Note that dynamic tests are quite different from standard {@link Test @Test}
* cases since callbacks such as {@link BeforeEach @BeforeEach} and
* {@link AfterEach @AfterEach} methods are not executed for dynamic tests.
*
* @since 5.0
* @see #dynamicTest(String, Executable)
* @see #stream(Iterator, Function, ThrowingConsumer)
* @see Test
* @see TestFactory
* @see DynamicContainer
* @see Executable
*/
@API(status = MAINTAINED, since = "5.3")
public class DynamicTest extends DynamicNode {
/**
* Factory for creating a new {@code DynamicTest} for the supplied display
* name and executable code block.
*
* @param displayName the display name for the dynamic test; never
* {@code null} or blank
* @param executable the executable code block for the dynamic test;
* never {@code null}
* @see #stream(Iterator, Function, ThrowingConsumer)
*/
public static DynamicTest dynamicTest(String displayName, Executable executable) {
return new DynamicTest(displayName, null, executable);
}
/**
* Factory for creating a new {@code DynamicTest} for the supplied display
* name, custom test source {@link URI}, and executable code block.
*
* @param displayName the display name for the dynamic test; never
* {@code null} or blank
* @param testSourceUri a custom test source URI for the dynamic test; may
* be {@code null} if the framework should generate the test source based on
* the {@code @TestFactory} method
* @param executable the executable code block for the dynamic test;
* never {@code null}
* @since 5.3
* @see #stream(Iterator, Function, ThrowingConsumer)
*/
public static DynamicTest dynamicTest(String displayName, URI testSourceUri, Executable executable) {
return new DynamicTest(displayName, testSourceUri, executable);
}
/**
* Generate a stream of dynamic tests based on the given generator and test
* executor.
*
*
Use this method when the set of dynamic tests is nondeterministic in
* nature or when the input comes from an existing {@link Iterator}. See
* {@link #stream(Stream, Function, ThrowingConsumer)} as an alternative.
*
*
The given {@code inputGenerator} is responsible for generating
* input values. A {@link DynamicTest} will be added to the resulting
* stream for each dynamically generated input value, using the given
* {@code displayNameGenerator} and {@code testExecutor}.
*
* @param inputGenerator an {@code Iterator} that serves as a dynamic
* input generator; never {@code null}
* @param displayNameGenerator a function that generates a display name
* based on an input value; never {@code null}
* @param testExecutor a consumer that executes a test based on an input
* value; never {@code null}
* @param the type of input generated by the {@code inputGenerator}
* and used by the {@code displayNameGenerator} and {@code testExecutor}
* @return a stream of dynamic tests based on the given generator and
* executor; never {@code null}
* @see #dynamicTest(String, Executable)
* @see #stream(Stream, Function, ThrowingConsumer)
*/
public static Stream stream(Iterator inputGenerator,
Function super T, String> displayNameGenerator, ThrowingConsumer super T> testExecutor) {
Preconditions.notNull(inputGenerator, "inputGenerator must not be null");
return stream(StreamSupport.stream(spliteratorUnknownSize(inputGenerator, ORDERED), false),
displayNameGenerator, testExecutor);
}
/**
* Generate a stream of dynamic tests based on the given input stream and
* test executor.
*
* Use this method when the set of dynamic tests is nondeterministic in
* nature or when the input comes from an existing {@link Stream}. See
* {@link #stream(Iterator, Function, ThrowingConsumer)} as an alternative.
*
*
The given {@code inputStream} is responsible for supplying input values.
* A {@link DynamicTest} will be added to the resulting stream for each
* dynamically supplied input value, using the given {@code displayNameGenerator}
* and {@code testExecutor}.
*
* @param inputStream a {@code Stream} that supplies dynamic input values;
* never {@code null}
* @param displayNameGenerator a function that generates a display name
* based on an input value; never {@code null}
* @param testExecutor a consumer that executes a test based on an input
* value; never {@code null}
* @param the type of input supplied by the {@code inputStream}
* and used by the {@code displayNameGenerator} and {@code testExecutor}
* @return a stream of dynamic tests based on the given generator and
* executor; never {@code null}
* @since 5.7
* @see #dynamicTest(String, Executable)
* @see #stream(Iterator, Function, ThrowingConsumer)
*/
@API(status = MAINTAINED, since = "5.7")
public static Stream stream(Stream inputStream,
Function super T, String> displayNameGenerator, ThrowingConsumer super T> testExecutor) {
Preconditions.notNull(inputStream, "inputStream must not be null");
Preconditions.notNull(displayNameGenerator, "displayNameGenerator must not be null");
Preconditions.notNull(testExecutor, "testExecutor must not be null");
return inputStream //
.map(input -> dynamicTest(displayNameGenerator.apply(input), () -> testExecutor.accept(input)));
}
/**
* Generate a stream of dynamic tests based on the given generator and test
* executor.
*
* Use this method when the set of dynamic tests is nondeterministic in
* nature or when the input comes from an existing {@link Iterator}. See
* {@link #stream(Stream, ThrowingConsumer)} as an alternative.
*
*
The given {@code inputGenerator} is responsible for generating
* input values and display names. A {@link DynamicTest} will be added to
* the resulting stream for each dynamically generated input value,
* using the given {@code testExecutor}.
*
* @param inputGenerator an {@code Iterator} with {@code Named} values
* that serves as a dynamic input generator; never {@code null}
* @param testExecutor a consumer that executes a test based on an input
* value; never {@code null}
* @param the type of input generated by the {@code inputGenerator}
* and used by the {@code testExecutor}
* @return a stream of dynamic tests based on the given generator and
* executor; never {@code null}
* @since 5.8
*
* @see #dynamicTest(String, Executable)
* @see #stream(Stream, ThrowingConsumer)
* @see Named
*/
@API(status = MAINTAINED, since = "5.8")
public static Stream stream(Iterator extends Named> inputGenerator,
ThrowingConsumer super T> testExecutor) {
Preconditions.notNull(inputGenerator, "inputGenerator must not be null");
return stream(StreamSupport.stream(spliteratorUnknownSize(inputGenerator, ORDERED), false), testExecutor);
}
/**
* Generate a stream of dynamic tests based on the given input stream and
* test executor.
*
* Use this method when the set of dynamic tests is nondeterministic in
* nature or when the input comes from an existing {@link Stream}. See
* {@link #stream(Iterator, ThrowingConsumer)} as an alternative.
*
*
The given {@code inputStream} is responsible for supplying input values
* and display names. A {@link DynamicTest} will be added to the resulting stream for
* each dynamically supplied input value, using the given {@code testExecutor}.
*
* @param inputStream a {@code Stream} that supplies dynamic {@code Named}
* input values; never {@code null}
* @param testExecutor a consumer that executes a test based on an input
* value; never {@code null}
* @param the type of input supplied by the {@code inputStream}
* and used by the {@code displayNameGenerator} and {@code testExecutor}
* @return a stream of dynamic tests based on the given generator and
* executor; never {@code null}
* @since 5.8
*
* @see #dynamicTest(String, Executable)
* @see #stream(Iterator, ThrowingConsumer)
* @see Named
*/
@API(status = MAINTAINED, since = "5.8")
public static Stream stream(Stream extends Named> inputStream,
ThrowingConsumer super T> testExecutor) {
Preconditions.notNull(inputStream, "inputStream must not be null");
Preconditions.notNull(testExecutor, "testExecutor must not be null");
return inputStream //
.map(input -> dynamicTest(input.getName(), () -> testExecutor.accept(input.getPayload())));
}
private final Executable executable;
private DynamicTest(String displayName, URI testSourceUri, Executable executable) {
super(displayName, testSourceUri);
this.executable = Preconditions.notNull(executable, "executable must not be null");
}
/**
* Get the {@code executable} code block associated with this {@code DynamicTest}.
*/
public Executable getExecutable() {
return this.executable;
}
}