/*
* Copyright 2015-2017 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 v1.0 which
* accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.junit.jupiter.engine;
import static java.util.Arrays.asList;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;
import static org.junit.platform.engine.test.event.ExecutionEventConditions.abortedWithReason;
import static org.junit.platform.engine.test.event.ExecutionEventConditions.assertRecordedExecutionEventsContainsExactly;
import static org.junit.platform.engine.test.event.ExecutionEventConditions.container;
import static org.junit.platform.engine.test.event.ExecutionEventConditions.displayName;
import static org.junit.platform.engine.test.event.ExecutionEventConditions.dynamicTestRegistered;
import static org.junit.platform.engine.test.event.ExecutionEventConditions.engine;
import static org.junit.platform.engine.test.event.ExecutionEventConditions.event;
import static org.junit.platform.engine.test.event.ExecutionEventConditions.finishedSuccessfully;
import static org.junit.platform.engine.test.event.ExecutionEventConditions.finishedWithFailure;
import static org.junit.platform.engine.test.event.ExecutionEventConditions.skippedWithReason;
import static org.junit.platform.engine.test.event.ExecutionEventConditions.started;
import static org.junit.platform.engine.test.event.ExecutionEventConditions.test;
import static org.junit.platform.engine.test.event.TestExecutionResultConditions.message;
import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.assertj.core.api.Condition;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ContainerExecutionCondition;
import org.junit.jupiter.api.extension.ContainerExtensionContext;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.Extension;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
import org.junit.jupiter.api.extension.TestExecutionCondition;
import org.junit.jupiter.api.extension.TestExecutionExceptionHandler;
import org.junit.jupiter.api.extension.TestExtensionContext;
import org.junit.jupiter.api.extension.TestInstancePostProcessor;
import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;
import org.junit.platform.engine.TestDescriptor;
import org.junit.platform.engine.test.event.ExecutionEvent;
import org.junit.platform.engine.test.event.ExecutionEventRecorder;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.opentest4j.AssertionFailedError;
/**
* @since 5.0
*/
class TestTemplateInvocationTests extends AbstractJupiterTestEngineTests {
@Test
void templateWithSingleRegisteredExtensionIsInvoked() {
LauncherDiscoveryRequest request = request().selectors(
selectMethod(MyTestTemplateTestCase.class, "templateWithSingleRegisteredExtension")).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
assertRecordedExecutionEventsContainsExactly(eventRecorder.getExecutionEvents(), //
wrappedInContainerEvents(MyTestTemplateTestCase.class, //
event(container("templateWithSingleRegisteredExtension"), started()), //
event(dynamicTestRegistered("test-template-invocation:#1")), //
event(test("test-template-invocation:#1"), started()), //
event(test("test-template-invocation:#1"),
finishedWithFailure(message("invocation is expected to fail"))), //
event(container("templateWithSingleRegisteredExtension"), finishedSuccessfully())));
}
@Test
void parentChildRelationshipIsEstablished() {
LauncherDiscoveryRequest request = request().selectors(
selectMethod(MyTestTemplateTestCase.class, "templateWithSingleRegisteredExtension")).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
TestDescriptor templateMethodDescriptor = findTestDescriptor(eventRecorder,
container("templateWithSingleRegisteredExtension"));
TestDescriptor invocationDescriptor = findTestDescriptor(eventRecorder, test("test-template-invocation:#1"));
assertThat(invocationDescriptor.getParent()).hasValue(templateMethodDescriptor);
assertThat(templateMethodDescriptor.getChildren()).isEqualTo(singleton(invocationDescriptor));
}
@Test
void beforeAndAfterEachMethodsAreExecutedAroundInvocation() {
LauncherDiscoveryRequest request = request().selectors(
selectMethod(TestTemplateTestClassWithBeforeAndAfterEach.class, "testTemplateWithTwoInvocations")).build();
executeTests(request);
assertThat(TestTemplateTestClassWithBeforeAndAfterEach.lifecycleEvents).containsExactly(
"beforeAll:TestTemplateInvocationTests$TestTemplateTestClassWithBeforeAndAfterEach", "beforeEach:[1]",
"afterEach:[1]", "beforeEach:[2]", "afterEach:[2]",
"afterAll:TestTemplateInvocationTests$TestTemplateTestClassWithBeforeAndAfterEach");
}
@Test
void templateWithTwoRegisteredExtensionsIsInvoked() {
LauncherDiscoveryRequest request = request().selectors(
selectMethod(MyTestTemplateTestCase.class, "templateWithTwoRegisteredExtensions")).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
assertRecordedExecutionEventsContainsExactly(eventRecorder.getExecutionEvents(), //
wrappedInContainerEvents(MyTestTemplateTestCase.class, //
event(container("templateWithTwoRegisteredExtensions"), started()), //
event(dynamicTestRegistered("test-template-invocation:#1"), displayName("[1]")), //
event(test("test-template-invocation:#1"), started()), //
event(test("test-template-invocation:#1"),
finishedWithFailure(message("invocation is expected to fail"))), //
event(dynamicTestRegistered("test-template-invocation:#2"), displayName("[2]")), //
event(test("test-template-invocation:#2"), started()), //
event(test("test-template-invocation:#2"),
finishedWithFailure(message("invocation is expected to fail"))), //
event(container("templateWithTwoRegisteredExtensions"), finishedSuccessfully())));
}
@Test
void templateWithTwoInvocationsFromSingleExtensionIsInvoked() {
LauncherDiscoveryRequest request = request().selectors(
selectMethod(MyTestTemplateTestCase.class, "templateWithTwoInvocationsFromSingleExtension")).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
assertRecordedExecutionEventsContainsExactly(eventRecorder.getExecutionEvents(), //
wrappedInContainerEvents(MyTestTemplateTestCase.class, //
event(container("templateWithTwoInvocationsFromSingleExtension"), started()), //
event(dynamicTestRegistered("test-template-invocation:#1"), displayName("[1]")), //
event(test("test-template-invocation:#1"), started()), //
event(test("test-template-invocation:#1"),
finishedWithFailure(message("invocation is expected to fail"))), //
event(dynamicTestRegistered("test-template-invocation:#2"), displayName("[2]")), //
event(test("test-template-invocation:#2"), started()), //
event(test("test-template-invocation:#2"),
finishedWithFailure(message("invocation is expected to fail"))), //
event(container("templateWithTwoInvocationsFromSingleExtension"), finishedSuccessfully())));
}
@Test
void templateWithDisabledInvocationsIsSkipped() {
LauncherDiscoveryRequest request = request().selectors(
selectMethod(MyTestTemplateTestCase.class, "templateWithDisabledInvocations")).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
assertRecordedExecutionEventsContainsExactly(eventRecorder.getExecutionEvents(), //
wrappedInContainerEvents(MyTestTemplateTestCase.class, //
event(container("templateWithDisabledInvocations"), started()), //
event(dynamicTestRegistered("test-template-invocation:#1")), //
event(test("test-template-invocation:#1"), skippedWithReason("tests are always disabled")), //
event(container("templateWithDisabledInvocations"), finishedSuccessfully())));
}
@Test
void disabledTemplateIsSkipped() {
LauncherDiscoveryRequest request = request().selectors(
selectMethod(MyTestTemplateTestCase.class, "disabledTemplate")).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
assertRecordedExecutionEventsContainsExactly(eventRecorder.getExecutionEvents(), //
wrappedInContainerEvents(MyTestTemplateTestCase.class, //
event(container("disabledTemplate"), skippedWithReason("containers are always disabled"))));
}
@Test
void templateWithCustomizedDisplayNamesIsInvoked() {
LauncherDiscoveryRequest request = request().selectors(
selectMethod(MyTestTemplateTestCase.class, "templateWithCustomizedDisplayNames")).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
assertRecordedExecutionEventsContainsExactly(eventRecorder.getExecutionEvents(), //
wrappedInContainerEvents(MyTestTemplateTestCase.class, //
event(container("templateWithCustomizedDisplayNames"), started()), //
event(dynamicTestRegistered("test-template-invocation:#1"),
displayName("1 --> templateWithCustomizedDisplayNames()")), //
event(test("test-template-invocation:#1"), started()), //
event(test("test-template-invocation:#1"),
finishedWithFailure(message("invocation is expected to fail"))), //
event(container("templateWithCustomizedDisplayNames"), finishedSuccessfully())));
}
@Test
void templateWithDynamicParameterResolverIsInvoked() {
LauncherDiscoveryRequest request = request().selectors(selectMethod(MyTestTemplateTestCase.class,
"templateWithDynamicParameterResolver", "java.lang.String")).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
assertRecordedExecutionEventsContainsExactly(eventRecorder.getExecutionEvents(), //
wrappedInContainerEvents(MyTestTemplateTestCase.class, //
event(container("templateWithDynamicParameterResolver"), started()), //
event(dynamicTestRegistered("test-template-invocation:#1"), displayName("[1] foo")), //
event(test("test-template-invocation:#1"), started()), //
event(test("test-template-invocation:#1"), finishedWithFailure(message("foo"))), //
event(dynamicTestRegistered("test-template-invocation:#2"), displayName("[2] bar")), //
event(test("test-template-invocation:#2"), started()), //
event(test("test-template-invocation:#2"), finishedWithFailure(message("bar"))), //
event(container("templateWithDynamicParameterResolver"), finishedSuccessfully())));
}
@Test
void contextParameterResolverCanResolveConstructorArguments() {
LauncherDiscoveryRequest request = request().selectors(
selectMethod(MyTestTemplateTestCaseWithConstructor.class, "template", "java.lang.String")).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
assertRecordedExecutionEventsContainsExactly(eventRecorder.getExecutionEvents(), //
wrappedInContainerEvents(MyTestTemplateTestCase.class, //
event(container("template"), started()), //
event(dynamicTestRegistered("test-template-invocation:#1"), displayName("[1] foo")), //
event(test("test-template-invocation:#1"), started()), //
event(test("test-template-invocation:#1"), finishedSuccessfully()), //
event(dynamicTestRegistered("test-template-invocation:#2"), displayName("[2] bar")), //
event(test("test-template-invocation:#2"), started()), //
event(test("test-template-invocation:#2"), finishedSuccessfully()), //
event(container("template"), finishedSuccessfully())));
}
@Test
void templateWithDynamicTestInstancePostProcessorIsInvoked() {
LauncherDiscoveryRequest request = request().selectors(
selectMethod(MyTestTemplateTestCase.class, "templateWithDynamicTestInstancePostProcessor")).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
assertRecordedExecutionEventsContainsExactly(eventRecorder.getExecutionEvents(), //
wrappedInContainerEvents(MyTestTemplateTestCase.class, //
event(container("templateWithDynamicTestInstancePostProcessor"), started()), //
event(dynamicTestRegistered("test-template-invocation:#1")), //
event(test("test-template-invocation:#1"), started()), //
event(test("test-template-invocation:#1"), finishedWithFailure(message("foo"))), //
event(dynamicTestRegistered("test-template-invocation:#2")), //
event(test("test-template-invocation:#2"), started()), //
event(test("test-template-invocation:#2"), finishedWithFailure(message("bar"))), //
event(container("templateWithDynamicTestInstancePostProcessor"), finishedSuccessfully())));
}
@Test
void lifecycleCallbacksAreExecutedForInvocation() {
LauncherDiscoveryRequest request = request().selectors(
selectClass(TestTemplateTestClassWithDynamicLifecycleCallbacks.class)).build();
executeTests(request);
// @formatter:off
assertThat(TestTemplateTestClassWithDynamicLifecycleCallbacks.lifecycleEvents).containsExactly(
"beforeEach",
"beforeTestExecution",
"testTemplate:foo",
"handleTestExecutionException",
"afterTestExecution",
"afterEach",
"beforeEach",
"beforeTestExecution",
"testTemplate:bar",
"afterTestExecution",
"afterEach");
// @formatter:on
}
@Test
void extensionIsAskedForSupportBeforeItMustProvide() {
LauncherDiscoveryRequest request = request().selectors(
selectMethod(MyTestTemplateTestCase.class, "templateWithWrongParameterType", int.class.getName())).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
assertRecordedExecutionEventsContainsExactly(eventRecorder.getExecutionEvents(), //
wrappedInContainerEvents(MyTestTemplateTestCase.class, //
event(container("templateWithWrongParameterType"), started()), //
event(container("templateWithWrongParameterType"), finishedWithFailure(message(s -> s.startsWith(
"You must register at least one TestTemplateInvocationContextProvider that supports @TestTemplate method ["))))));
}
@Test
void templateWithSupportingProviderButNoInvocationsReportsAbortedTest() {
LauncherDiscoveryRequest request = request().selectors(
selectMethod(MyTestTemplateTestCase.class, "templateWithSupportingProviderButNoInvocations")).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
assertRecordedExecutionEventsContainsExactly(eventRecorder.getExecutionEvents(), //
wrappedInContainerEvents(MyTestTemplateTestCase.class, //
event(container("templateWithSupportingProviderButNoInvocations"), started()), //
event(container("templateWithSupportingProviderButNoInvocations"), abortedWithReason(
message("No supporting TestTemplateInvocationContextProvider provided an invocation context")))));
}
@Test
void templateWithCloseableStream() {
LauncherDiscoveryRequest request = request().selectors(
selectMethod(MyTestTemplateTestCase.class, "templateWithCloseableStream")).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
assertThat(InvocationContextProviderWithCloseableStream.streamClosed.get()).describedAs(
"streamClosed").isTrue();
assertRecordedExecutionEventsContainsExactly(eventRecorder.getExecutionEvents(), //
wrappedInContainerEvents(MyTestTemplateTestCase.class, //
event(container("templateWithCloseableStream"), started()), //
event(dynamicTestRegistered("test-template-invocation:#1")), //
event(test("test-template-invocation:#1"), started()), //
event(test("test-template-invocation:#1"), finishedSuccessfully()), //
event(container("templateWithCloseableStream"), finishedSuccessfully())));
}
private TestDescriptor findTestDescriptor(ExecutionEventRecorder eventRecorder,
Condition<ExecutionEvent> condition) {
// @formatter:off
return eventRecorder.eventStream()
.filter(condition::matches)
.findAny()
.map(ExecutionEvent::getTestDescriptor)
.orElseThrow(() -> new AssertionFailedError("Could not find execution event for condition: " + condition));
// @formatter:on
}
@SuppressWarnings({ "unchecked", "varargs", "rawtypes" })
@SafeVarargs
private final Condition<? super ExecutionEvent>[] wrappedInContainerEvents(Class<MyTestTemplateTestCase> clazz,
Condition<? super ExecutionEvent>... wrappedConditions) {
List<Condition<? super ExecutionEvent>> conditions = new ArrayList<>();
conditions.add(event(engine(), started()));
conditions.add(event(container(clazz), started()));
conditions.addAll(asList(wrappedConditions));
conditions.add(event(container(clazz), finishedSuccessfully()));
conditions.add(event(engine(), finishedSuccessfully()));
return conditions.toArray(new Condition[0]);
}
static class MyTestTemplateTestCase {
@TestTemplate
void templateWithoutRegisteredExtension() {
}
@ExtendWith(SingleInvocationContextProvider.class)
@TestTemplate
void templateWithSingleRegisteredExtension() {
fail("invocation is expected to fail");
}
@ExtendWith({ SingleInvocationContextProvider.class,
AnotherInvocationContextProviderWithASingleInvocation.class })
@TestTemplate
void templateWithTwoRegisteredExtensions() {
fail("invocation is expected to fail");
}
@ExtendWith(TwoInvocationsContextProvider.class)
@TestTemplate
void templateWithTwoInvocationsFromSingleExtension() {
fail("invocation is expected to fail");
}
@ExtendWith({ SingleInvocationContextProvider.class, AlwaysDisabledTestExecutionCondition.class })
@TestTemplate
void templateWithDisabledInvocations() {
fail("this is never called");
}
@ExtendWith(AlwaysDisabledContainerExecutionCondition.class)
@TestTemplate
void disabledTemplate() {
fail("this is never called");
}
@ExtendWith(InvocationContextProviderWithCustomizedDisplayNames.class)
@TestTemplate
void templateWithCustomizedDisplayNames() {
fail("invocation is expected to fail");
}
@ExtendWith(StringParameterResolvingInvocationContextProvider.class)
@TestTemplate
void templateWithDynamicParameterResolver(String parameter) {
fail(parameter);
}
@ExtendWith(StringParameterResolvingInvocationContextProvider.class)
@TestTemplate
void templateWithWrongParameterType(int parameter) {
fail("never called: " + parameter);
}
String parameterInstanceVariable;
@ExtendWith(StringParameterInjectingInvocationContextProvider.class)
@TestTemplate
void templateWithDynamicTestInstancePostProcessor() {
fail(parameterInstanceVariable);
}
@ExtendWith(InvocationContextProviderThatSupportsEverythingButProvidesNothing.class)
@TestTemplate
void templateWithSupportingProviderButNoInvocations() {
fail("never called");
}
@ExtendWith(InvocationContextProviderWithCloseableStream.class)
@TestTemplate
void templateWithCloseableStream() {
}
}
@ExtendWith(StringParameterResolvingInvocationContextProvider.class)
static class MyTestTemplateTestCaseWithConstructor {
private final String constructorParameter;
MyTestTemplateTestCaseWithConstructor(String constructorParameter) {
this.constructorParameter = constructorParameter;
}
@TestTemplate
void template(String parameter) {
assertEquals(constructorParameter, parameter);
}
}
static class TestTemplateTestClassWithBeforeAndAfterEach {
private static List<String> lifecycleEvents = new ArrayList<>();
@BeforeAll
static void beforeAll(TestInfo testInfo) {
lifecycleEvents.add("beforeAll:" + testInfo.getDisplayName());
}
@AfterAll
static void afterAll(TestInfo testInfo) {
lifecycleEvents.add("afterAll:" + testInfo.getDisplayName());
}
@BeforeEach
void beforeEach(TestInfo testInfo) {
lifecycleEvents.add("beforeEach:" + testInfo.getDisplayName());
}
@AfterEach
void afterEach(TestInfo testInfo) {
lifecycleEvents.add("afterEach:" + testInfo.getDisplayName());
}
@ExtendWith(TwoInvocationsContextProvider.class)
@TestTemplate
void testTemplateWithTwoInvocations() {
fail("invocation is expected to fail");
}
}
static class TestTemplateTestClassWithDynamicLifecycleCallbacks {
private static List<String> lifecycleEvents = new ArrayList<>();
@ExtendWith(InvocationContextProviderWithDynamicLifecycleCallbacks.class)
@TestTemplate
void testTemplate(TestInfo testInfo) {
lifecycleEvents.add("testTemplate:" + testInfo.getDisplayName());
assertEquals("bar", testInfo.getDisplayName());
}
}
private static class SingleInvocationContextProvider implements TestTemplateInvocationContextProvider {
@Override
public boolean supportsTestTemplate(ContainerExtensionContext context) {
return true;
}
@Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
ContainerExtensionContext context) {
return Stream.of(emptyTestTemplateInvocationContext());
}
}
private static class AnotherInvocationContextProviderWithASingleInvocation
implements TestTemplateInvocationContextProvider {
@Override
public boolean supportsTestTemplate(ContainerExtensionContext context) {
return true;
}
@Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
ContainerExtensionContext context) {
return Stream.of(emptyTestTemplateInvocationContext());
}
}
private static class TwoInvocationsContextProvider implements TestTemplateInvocationContextProvider {
@Override
public boolean supportsTestTemplate(ContainerExtensionContext context) {
return true;
}
@Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
ContainerExtensionContext context) {
return Stream.of(emptyTestTemplateInvocationContext(), emptyTestTemplateInvocationContext());
}
}
private static class AlwaysDisabledTestExecutionCondition implements TestExecutionCondition {
@Override
public ConditionEvaluationResult evaluateTestExecutionCondition(TestExtensionContext context) {
return ConditionEvaluationResult.disabled("tests are always disabled");
}
}
private static class AlwaysDisabledContainerExecutionCondition implements ContainerExecutionCondition {
@Override
public ConditionEvaluationResult evaluateContainerExecutionCondition(ContainerExtensionContext context) {
return ConditionEvaluationResult.disabled("containers are always disabled");
}
}
private static class InvocationContextProviderWithCustomizedDisplayNames
implements TestTemplateInvocationContextProvider {
@Override
public boolean supportsTestTemplate(ContainerExtensionContext context) {
return true;
}
@Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
ContainerExtensionContext context) {
return Stream.<TestTemplateInvocationContext> generate(() -> new TestTemplateInvocationContext() {
@Override
public String getDisplayName(int invocationIndex) {
return invocationIndex + " --> " + context.getDisplayName();
}
}).limit(1);
}
}
private static class StringParameterResolvingInvocationContextProvider
implements TestTemplateInvocationContextProvider {
@Override
public boolean supportsTestTemplate(ContainerExtensionContext context) {
// @formatter:off
return context.getTestMethod()
.map(Method::getParameterTypes)
.map(Arrays::stream)
.map(parameters -> parameters.anyMatch(Predicate.isEqual(String.class)))
.orElse(false);
// @formatter:on
}
@Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
ContainerExtensionContext context) {
return Stream.of(createContext("foo"), createContext("bar"));
}
private TestTemplateInvocationContext createContext(String argument) {
return new TestTemplateInvocationContext() {
@Override
public String getDisplayName(int invocationIndex) {
return TestTemplateInvocationContext.super.getDisplayName(invocationIndex) + " " + argument;
}
@Override
public List<Extension> getAdditionalExtensions() {
return singletonList(new ParameterResolver() {
@Override
public boolean supportsParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
return true;
}
@Override
public Object resolveParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
return argument;
}
});
}
};
}
}
private static class StringParameterInjectingInvocationContextProvider
implements TestTemplateInvocationContextProvider {
@Override
public boolean supportsTestTemplate(ContainerExtensionContext context) {
return true;
}
@Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
ContainerExtensionContext context) {
return Stream.of(createContext("foo"), createContext("bar"));
}
private TestTemplateInvocationContext createContext(String argument) {
return new TestTemplateInvocationContext() {
@Override
public String getDisplayName(int invocationIndex) {
return TestTemplateInvocationContext.super.getDisplayName(invocationIndex) + " " + argument;
}
@Override
public List<Extension> getAdditionalExtensions() {
return singletonList((TestInstancePostProcessor) (testInstance, context) -> {
Field field = testInstance.getClass().getDeclaredField("parameterInstanceVariable");
field.setAccessible(true);
field.set(testInstance, argument);
});
}
};
}
}
private static class InvocationContextProviderWithDynamicLifecycleCallbacks
implements TestTemplateInvocationContextProvider {
@Override
public boolean supportsTestTemplate(ContainerExtensionContext context) {
return true;
}
@Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
ContainerExtensionContext context) {
return Stream.of(createContext("foo"), createContext("bar"));
}
private TestTemplateInvocationContext createContext(String argument) {
return new TestTemplateInvocationContext() {
@Override
public String getDisplayName(int invocationIndex) {
return argument;
}
@Override
public List<Extension> getAdditionalExtensions() {
return singletonList(new LifecycleCallbackExtension());
}
};
}
private static class LifecycleCallbackExtension implements BeforeEachCallback, BeforeTestExecutionCallback,
TestExecutionExceptionHandler, AfterTestExecutionCallback, AfterEachCallback {
@Override
public void beforeEach(TestExtensionContext context) throws Exception {
TestTemplateTestClassWithDynamicLifecycleCallbacks.lifecycleEvents.add("beforeEach");
}
@Override
public void beforeTestExecution(TestExtensionContext context) throws Exception {
TestTemplateTestClassWithDynamicLifecycleCallbacks.lifecycleEvents.add("beforeTestExecution");
}
@Override
public void handleTestExecutionException(TestExtensionContext context, Throwable throwable)
throws Throwable {
TestTemplateTestClassWithDynamicLifecycleCallbacks.lifecycleEvents.add("handleTestExecutionException");
throw new AssertionError(throwable);
}
@Override
public void afterTestExecution(TestExtensionContext context) throws Exception {
TestTemplateTestClassWithDynamicLifecycleCallbacks.lifecycleEvents.add("afterTestExecution");
}
@Override
public void afterEach(TestExtensionContext context) throws Exception {
TestTemplateTestClassWithDynamicLifecycleCallbacks.lifecycleEvents.add("afterEach");
}
}
}
private static class InvocationContextProviderThatSupportsEverythingButProvidesNothing
implements TestTemplateInvocationContextProvider {
@Override
public boolean supportsTestTemplate(ContainerExtensionContext context) {
return true;
}
@Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
ContainerExtensionContext context) {
return Stream.empty();
}
}
private static class InvocationContextProviderWithCloseableStream implements TestTemplateInvocationContextProvider {
private static AtomicBoolean streamClosed = new AtomicBoolean(false);
@Override
public boolean supportsTestTemplate(ContainerExtensionContext context) {
return true;
}
@Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
ContainerExtensionContext context) {
return Stream.of(emptyTestTemplateInvocationContext()).onClose(() -> streamClosed.set(true));
}
}
private static TestTemplateInvocationContext emptyTestTemplateInvocationContext() {
return new TestTemplateInvocationContext() {
};
}
}