blob: 62a018985741ff3a7c3ebfa94b70a18ab12e7377 [file]
/*
* Copyright 2015-2022 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.
*
* <p>It is composed of a {@linkplain DynamicNode#getDisplayName display name}
* and an {@link #getExecutable Executable}.
*
* <p>Instances of {@code DynamicTest} must be generated by factory methods
* annotated with {@link TestFactory @TestFactory}.
*
* <p>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.
*
* <p>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.
*
* <p>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
* <em>input generator</em>; 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 <T> the type of <em>input</em> 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 <T> Stream<DynamicTest> stream(Iterator<T> 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.
*
* <p>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.
*
* <p>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 <T> the type of <em>input</em> 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 <T> Stream<DynamicTest> stream(Stream<T> 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.
*
* <p>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.
*
* <p>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 <em>input generator</em>; never {@code null}
* @param testExecutor a consumer that executes a test based on an input
* value; never {@code null}
* @param <T> the type of <em>input</em> 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 <T> Stream<DynamicTest> stream(Iterator<? extends Named<T>> 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.
*
* <p>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.
*
* <p>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 <T> the type of <em>input</em> 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 <T> Stream<DynamicTest> stream(Stream<? extends Named<T>> 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;
}
}