getParent();
/**
* Get the root {@code ExtensionContext}.
*
* @return the root extension context; never {@code null} but potentially
* this {@code ExtensionContext}
* @see #getParent()
*/
ExtensionContext getRoot();
/**
* Get the unique ID of the current test or container.
*
* @return the unique ID of the test or container; never {@code null} or blank
*/
String getUniqueId();
/**
* Get the display name for the current test or container.
*
* The display name is either a default name or a custom name configured
* via {@link org.junit.jupiter.api.DisplayName @DisplayName}.
*
*
For details on default display names consult the Javadoc for
* {@link org.junit.jupiter.api.TestInfo#getDisplayName()}.
*
*
Note that display names are typically used for test reporting in IDEs
* and build tools and may contain spaces, special characters, and even emoji.
*
* @return the display name of the test or container; never {@code null} or blank
*/
String getDisplayName();
/**
* Get the set of all tags for the current test or container.
*
*
Tags may be declared directly on the test element or inherited
* from an outer context.
*
* @return the set of tags for the test or container; never {@code null} but
* potentially empty
*/
Set getTags();
/**
* Get the {@link AnnotatedElement} corresponding to the current extension
* context, if available.
*
* For example, if the current extension context encapsulates a test
* class, test method, test factory method, or test template method, the
* annotated element will be the corresponding {@link Class} or {@link Method}
* reference.
*
*
Favor this method over more specific methods whenever the
* {@code AnnotatedElement} API suits the task at hand — for example,
* when looking up annotations regardless of concrete element type.
*
* @return an {@code Optional} containing the {@code AnnotatedElement};
* never {@code null} but potentially empty
* @see #getTestClass()
* @see #getTestMethod()
*/
Optional getElement();
/**
* Get the {@link Class} associated with the current test or container,
* if available.
*
* @return an {@code Optional} containing the class; never {@code null} but
* potentially empty
* @see #getRequiredTestClass()
*/
Optional> getTestClass();
/**
* Get the required {@link Class} associated with the current test
* or container.
*
* Use this method as an alternative to {@link #getTestClass()} for use
* cases in which the test class is required to be present.
*
* @return the test class; never {@code null}
* @throws PreconditionViolationException if the test class is not present
* in this {@code ExtensionContext}
*/
default Class> getRequiredTestClass() {
return Preconditions.notNull(getTestClass().orElse(null),
"Illegal state: required test class is not present in the current ExtensionContext");
}
/**
* Get the {@link Lifecycle} of the {@linkplain #getTestInstance() test
* instance} associated with the current test or container, if available.
*
* @return an {@code Optional} containing the test instance {@code Lifecycle};
* never {@code null} but potentially empty
* @since 5.1
* @see org.junit.jupiter.api.TestInstance {@code @TestInstance}
*/
@API(status = STABLE, since = "5.1")
Optional getTestInstanceLifecycle();
/**
* Get the test instance associated with the current test or container,
* if available.
*
* @return an {@code Optional} containing the test instance; never
* {@code null} but potentially empty
* @see #getRequiredTestInstance()
* @see #getTestInstances()
*/
Optional getTestInstance();
/**
* Get the required test instance associated with the current test
* or container.
*
* Use this method as an alternative to {@link #getTestInstance()} for use
* cases in which the test instance is required to be present.
*
* @return the test instance; never {@code null}
* @throws PreconditionViolationException if the test instance is not present
* in this {@code ExtensionContext}
*
* @see #getRequiredTestInstances()
*/
default Object getRequiredTestInstance() {
return Preconditions.notNull(getTestInstance().orElse(null),
"Illegal state: required test instance is not present in the current ExtensionContext");
}
/**
* Get the test instances associated with the current test or container,
* if available.
*
*
While top-level tests only have a single test instance, nested tests
* have one additional instance for each enclosing test class.
*
* @return an {@code Optional} containing the test instances; never
* {@code null} but potentially empty
* @since 5.4
* @see #getRequiredTestInstances()
*/
@API(status = STABLE, since = "5.7")
Optional getTestInstances();
/**
* Get the required test instances associated with the current test
* or container.
*
* Use this method as an alternative to {@link #getTestInstances()} for use
* cases in which the test instances are required to be present.
*
* @return the test instances; never {@code null}
* @throws PreconditionViolationException if the test instances are not present
* in this {@code ExtensionContext}
* @since 5.4
*/
@API(status = STABLE, since = "5.7")
default TestInstances getRequiredTestInstances() {
return Preconditions.notNull(getTestInstances().orElse(null),
"Illegal state: required test instances are not present in the current ExtensionContext");
}
/**
* Get the {@link Method} associated with the current test, if available.
*
* @return an {@code Optional} containing the method; never {@code null} but
* potentially empty
* @see #getRequiredTestMethod()
*/
Optional getTestMethod();
/**
* Get the required {@link Method} associated with the current test
* or container.
*
* Use this method as an alternative to {@link #getTestMethod()} for use
* cases in which the test method is required to be present.
*
* @return the test method; never {@code null}
* @throws PreconditionViolationException if the test method is not present
* in this {@code ExtensionContext}
*/
default Method getRequiredTestMethod() {
return Preconditions.notNull(getTestMethod().orElse(null),
"Illegal state: required test method is not present in the current ExtensionContext");
}
/**
* Get the exception that was thrown during execution of the test or container
* associated with this {@code ExtensionContext}, if available.
*
*
This method is typically used for logging and tracing purposes. If you
* wish to actually handle an exception thrown during test execution,
* implement the {@link TestExecutionExceptionHandler} API.
*
*
Unlike the exception passed to a {@code TestExecutionExceptionHandler},
* an execution exception returned by this method can be any
* exception thrown during the invocation of a {@code @Test} method, its
* surrounding {@code @BeforeEach} and {@code @AfterEach} methods, or a
* test-level {@link Extension}. Similarly, if this {@code ExtensionContext}
* represents a test class, the execution exception returned by
* this method can be any exception thrown in a {@code @BeforeAll} or
* {@code AfterAll} method or a class-level {@link Extension}.
*
*
Note, however, that this method will never return an exception
* swallowed by a {@code TestExecutionExceptionHandler}. Furthermore, if
* multiple exceptions have been thrown during test execution, the exception
* returned by this method will be the first such exception with all
* additional exceptions {@linkplain Throwable#addSuppressed(Throwable)
* suppressed} in the first one.
*
* @return an {@code Optional} containing the exception thrown; never
* {@code null} but potentially empty if test execution has not (yet)
* resulted in an exception
*/
Optional getExecutionException();
/**
* Get the configuration parameter stored under the specified {@code key}.
*
* If no such key is present in the {@code ConfigurationParameters} for
* the JUnit Platform, an attempt will be made to look up the value as a
* JVM system property. If no such system property exists, an attempt will
* be made to look up the value in the JUnit Platform properties file.
*
* @param key the key to look up; never {@code null} or blank
* @return an {@code Optional} containing the value; never {@code null}
* but potentially empty
*
* @since 5.1
* @see System#getProperty(String)
* @see org.junit.platform.engine.ConfigurationParameters
*/
@API(status = STABLE, since = "5.1")
Optional getConfigurationParameter(String key);
/**
* Get and transform the configuration parameter stored under the specified
* {@code key} using the specified {@code transformer}.
*
* If no such key is present in the {@code ConfigurationParameters} for
* the JUnit Platform, an attempt will be made to look up the value as a
* JVM system property. If no such system property exists, an attempt will
* be made to look up the value in the JUnit Platform properties file.
*
*
In case the transformer throws an exception, it will be wrapped in a
* {@link org.junit.platform.commons.JUnitException} with a helpful message.
*
* @param key the key to look up; never {@code null} or blank
* @param transformer the transformer to apply in case a value is found;
* never {@code null}
* @return an {@code Optional} containing the value; never {@code null}
* but potentially empty
*
* @since 5.7
* @see System#getProperty(String)
* @see org.junit.platform.engine.ConfigurationParameters
*/
@API(status = EXPERIMENTAL, since = "5.7")
Optional getConfigurationParameter(String key, Function transformer);
/**
* Publish a map of key-value pairs to be consumed by an
* {@code org.junit.platform.engine.EngineExecutionListener} in order to
* supply additional information to the reporting infrastructure.
*
* @param map the key-value pairs to be published; never {@code null};
* keys and values within entries in the map also must not be
* {@code null} or blank
* @see #publishReportEntry(String, String)
* @see #publishReportEntry(String)
* @see org.junit.platform.engine.EngineExecutionListener#reportingEntryPublished
*/
void publishReportEntry(Map map);
/**
* Publish the specified key-value pair to be consumed by an
* {@code org.junit.platform.engine.EngineExecutionListener} in order to
* supply additional information to the reporting infrastructure.
*
* @param key the key of the published pair; never {@code null} or blank
* @param value the value of the published pair; never {@code null} or blank
* @see #publishReportEntry(Map)
* @see #publishReportEntry(String)
* @see org.junit.platform.engine.EngineExecutionListener#reportingEntryPublished
*/
default void publishReportEntry(String key, String value) {
this.publishReportEntry(Collections.singletonMap(key, value));
}
/**
* Publish the specified value to be consumed by an
* {@code org.junit.platform.engine.EngineExecutionListener} in order to
* supply additional information to the reporting infrastructure.
*
* This method delegates to {@link #publishReportEntry(String, String)},
* supplying {@code "value"} as the key and the supplied {@code value}
* argument as the value.
*
* @param value the value to be published; never {@code null} or blank
* @since 5.3
* @see #publishReportEntry(Map)
* @see #publishReportEntry(String, String)
* @see org.junit.platform.engine.EngineExecutionListener#reportingEntryPublished
*/
@API(status = STABLE, since = "5.3")
default void publishReportEntry(String value) {
this.publishReportEntry("value", value);
}
/**
* Get the {@link Store} for the supplied {@link Namespace}.
*
*
Use {@code getStore(Namespace.GLOBAL)} to get the default, global {@link Namespace}.
*
*
A store is bound to its extension context lifecycle. When an extension
* context lifecycle ends it closes its associated store. All stored values
* that are instances of {@link ExtensionContext.Store.CloseableResource} are
* notified by invoking their {@code close()} methods.
*
* @param namespace the {@code Namespace} to get the store for; never {@code null}
* @return the store in which to put and get objects for other invocations
* working in the same namespace; never {@code null}
* @see Namespace#GLOBAL
*/
Store getStore(Namespace namespace);
/**
* Get the {@link ExecutionMode} associated with the current test or container.
*
* @return the {@code ExecutionMode} of the test; never {@code null}
*
* @since 5.8.1
* @see org.junit.jupiter.api.parallel.ExecutionMode {@code @ExecutionMode}
*/
@API(status = STABLE, since = "5.8.1")
ExecutionMode getExecutionMode();
/**
* Get an {@link ExecutableInvoker} to invoke methods and constructors
* with support for dynamic resolution of parameters.
*
* @since 5.9
*/
@API(status = EXPERIMENTAL, since = "5.9")
ExecutableInvoker getExecutableInvoker();
/**
* {@code Store} provides methods for extensions to save and retrieve data.
*/
interface Store {
/**
* Classes implementing this interface indicate that they want to {@link #close}
* some underlying resource or resources when the enclosing {@link Store Store}
* is closed.
*
*
Note that the {@code CloseableResource} API is only honored for
* objects stored within an extension context {@link Store Store}.
*
*
The resources stored in a {@link Store Store} are closed in the
* inverse order they were added in.
*
* @since 5.1
*/
@API(status = STABLE, since = "5.1")
interface CloseableResource {
/**
* Close underlying resources.
*
* @throws Throwable any throwable will be caught and rethrown
*/
void close() throws Throwable;
}
/**
* Get the value that is stored under the supplied {@code key}.
*
*
If no value is stored in the current {@link ExtensionContext}
* for the supplied {@code key}, ancestors of the context will be queried
* for a value with the same {@code key} in the {@code Namespace} used
* to create this store.
*
*
For greater type safety, consider using {@link #get(Object, Class)}
* instead.
*
* @param key the key; never {@code null}
* @return the value; potentially {@code null}
* @see #get(Object, Class)
* @see #getOrDefault(Object, Class, Object)
*/
Object get(Object key);
/**
* Get the value of the specified required type that is stored under
* the supplied {@code key}.
*
*
If no value is stored in the current {@link ExtensionContext}
* for the supplied {@code key}, ancestors of the context will be queried
* for a value with the same {@code key} in the {@code Namespace} used
* to create this store.
*
* @param key the key; never {@code null}
* @param requiredType the required type of the value; never {@code null}
* @param the value type
* @return the value; potentially {@code null}
* @see #get(Object)
* @see #getOrDefault(Object, Class, Object)
*/
V get(Object key, Class requiredType);
/**
* Get the value of the specified required type that is stored under
* the supplied {@code key}, or the supplied {@code defaultValue} if no
* value is found for the supplied {@code key} in this store or in an
* ancestor.
*
* If no value is stored in the current {@link ExtensionContext}
* for the supplied {@code key}, ancestors of the context will be queried
* for a value with the same {@code key} in the {@code Namespace} used
* to create this store.
*
* @param key the key; never {@code null}
* @param requiredType the required type of the value; never {@code null}
* @param defaultValue the default value
* @param the value type
* @return the value; potentially {@code null}
* @since 5.5
* @see #get(Object, Class)
*/
@API(status = STABLE, since = "5.5")
default V getOrDefault(Object key, Class requiredType, V defaultValue) {
V value = get(key, requiredType);
return (value != null ? value : defaultValue);
}
/**
* Get the object of type {@code type} that is present in this
* {@code Store} (keyed by {@code type}); and otherwise invoke
* the default constructor for {@code type} to generate the object,
* store it, and return it.
*
* This method is a shortcut for the following, where {@code X} is
* the type of object we wish to retrieve from the store.
*
*
* X x = store.getOrComputeIfAbsent(X.class, key -> new X(), X.class);
* // Equivalent to:
* // X x = store.getOrComputeIfAbsent(X.class);
*
*
* See {@link #getOrComputeIfAbsent(Object, Function, Class)} for
* further details.
*
*
If {@code type} implements {@link ExtensionContext.Store.CloseableResource}
* the {@code close()} method will be invoked on the stored object when
* the store is closed.
*
* @param type the type of object to retrieve; never {@code null}
* @param the key and value type
* @return the object; never {@code null}
* @since 5.1
* @see #getOrComputeIfAbsent(Object, Function)
* @see #getOrComputeIfAbsent(Object, Function, Class)
* @see CloseableResource
*/
@API(status = STABLE, since = "5.1")
default V getOrComputeIfAbsent(Class type) {
return getOrComputeIfAbsent(type, ReflectionSupport::newInstance, type);
}
/**
* Get the value that is stored under the supplied {@code key}.
*
* If no value is stored in the current {@link ExtensionContext}
* for the supplied {@code key}, ancestors of the context will be queried
* for a value with the same {@code key} in the {@code Namespace} used
* to create this store. If no value is found for the supplied {@code key},
* a new value will be computed by the {@code defaultCreator} (given
* the {@code key} as input), stored, and returned.
*
*
For greater type safety, consider using
* {@link #getOrComputeIfAbsent(Object, Function, Class)} instead.
*
*
If the created value is an instance of {@link ExtensionContext.Store.CloseableResource}
* the {@code close()} method will be invoked on the stored object when
* the store is closed.
*
* @param key the key; never {@code null}
* @param defaultCreator the function called with the supplied {@code key}
* to create a new value; never {@code null}
* @param the key type
* @param the value type
* @return the value; potentially {@code null}
* @see #getOrComputeIfAbsent(Class)
* @see #getOrComputeIfAbsent(Object, Function, Class)
* @see CloseableResource
*/
Object getOrComputeIfAbsent(K key, Function defaultCreator);
/**
* Get the value of the specified required type that is stored under the
* supplied {@code key}.
*
* If no value is stored in the current {@link ExtensionContext}
* for the supplied {@code key}, ancestors of the context will be queried
* for a value with the same {@code key} in the {@code Namespace} used
* to create this store. If no value is found for the supplied {@code key},
* a new value will be computed by the {@code defaultCreator} (given
* the {@code key} as input), stored, and returned.
*
*
If {@code requiredType} implements {@link ExtensionContext.Store.CloseableResource}
* the {@code close()} method will be invoked on the stored object when
* the store is closed.
*
* @param key the key; never {@code null}
* @param defaultCreator the function called with the supplied {@code key}
* to create a new value; never {@code null}
* @param requiredType the required type of the value; never {@code null}
* @param the key type
* @param the value type
* @return the value; potentially {@code null}
* @see #getOrComputeIfAbsent(Class)
* @see #getOrComputeIfAbsent(Object, Function)
* @see CloseableResource
*/
V getOrComputeIfAbsent(K key, Function defaultCreator, Class requiredType);
/**
* Store a {@code value} for later retrieval under the supplied {@code key}.
*
* A stored {@code value} is visible in child {@link ExtensionContext
* ExtensionContexts} for the store's {@code Namespace} unless they
* overwrite it.
*
*
If the {@code value} is an instance of {@link ExtensionContext.Store.CloseableResource}
* the {@code close()} method will be invoked on the stored object when
* the store is closed.
*
* @param key the key under which the value should be stored; never
* {@code null}
* @param value the value to store; may be {@code null}
* @see CloseableResource
*/
void put(Object key, Object value);
/**
* Remove the value that was previously stored under the supplied {@code key}.
*
*
The value will only be removed in the current {@link ExtensionContext},
* not in ancestors. In addition, the {@link CloseableResource} API will not
* be honored for values that are manually removed via this method.
*
*
For greater type safety, consider using {@link #remove(Object, Class)}
* instead.
*
* @param key the key; never {@code null}
* @return the previous value or {@code null} if no value was present
* for the specified key
* @see #remove(Object, Class)
*/
Object remove(Object key);
/**
* Remove the value of the specified required type that was previously stored
* under the supplied {@code key}.
*
*
The value will only be removed in the current {@link ExtensionContext},
* not in ancestors. In addition, the {@link CloseableResource} API will not
* be honored for values that are manually removed via this method.
*
* @param key the key; never {@code null}
* @param requiredType the required type of the value; never {@code null}
* @param the value type
* @return the previous value or {@code null} if no value was present
* for the specified key
* @see #remove(Object)
*/
V remove(Object key, Class requiredType);
}
/**
* A {@code Namespace} is used to provide a scope for data saved by
* extensions within a {@link Store}.
*
* Storing data in custom namespaces allows extensions to avoid accidentally
* mixing data between extensions or across different invocations within the
* lifecycle of a single extension.
*/
class Namespace {
/**
* The default, global namespace which allows access to stored data from
* all extensions.
*/
public static final Namespace GLOBAL = Namespace.create(new Object());
/**
* Create a namespace which restricts access to data to all extensions
* which use the same sequence of {@code parts} for creating a namespace.
*
*
The order of the {@code parts} is significant.
*
*
Internally the {@code parts} are compared using {@link Object#equals(Object)}.
*/
public static Namespace create(Object... parts) {
Preconditions.notEmpty(parts, "parts array must not be null or empty");
Preconditions.containsNoNullElements(parts, "individual parts must not be null");
return new Namespace(new ArrayList<>(Arrays.asList(parts)));
}
private final List parts;
private Namespace(List parts) {
this.parts = parts;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Namespace that = (Namespace) o;
return this.parts.equals(that.parts);
}
@Override
public int hashCode() {
return this.parts.hashCode();
}
/**
* Create a new namespace by appending the supplied {@code parts} to the
* existing sequence of parts in this namespace.
*
* @return new namespace; never {@code null}
* @since 5.8
*/
@API(status = EXPERIMENTAL, since = "5.8")
public Namespace append(Object... parts) {
Preconditions.notEmpty(parts, "parts array must not be null or empty");
Preconditions.containsNoNullElements(parts, "individual parts must not be null");
ArrayList newParts = new ArrayList<>(this.parts.size() + parts.length);
newParts.addAll(this.parts);
Collections.addAll(newParts, parts);
return new Namespace(newParts);
}
}
}