diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestInstance.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestInstance.java
index 37516dd95a18..1ef173f3cede 100644
--- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestInstance.java
+++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestInstance.java
@@ -25,7 +25,11 @@
*
If {@code @TestInstance} is not declared on a test class or implemented
* test interface, the lifecycle mode will implicitly default to
* {@link Lifecycle#PER_METHOD PER_METHOD}. Note, however, that an explicit
- * lifecycle mode is inherited within a test class hierarchy.
+ * lifecycle mode is inherited within a test class hierarchy. In
+ * addition, the default lifecycle mode may be set via the
+ * {@code junit.jupiter.testinstance.lifecycle.default} configuration
+ * parameter which can be supplied via the {@code Launcher} API or via a
+ * JVM system property. Consult the User Guide for further information.
*
*
Use Cases
* Setting the test instance lifecycle mode to {@link Lifecycle#PER_CLASS
@@ -57,6 +61,9 @@
/**
* Enumeration of test instance lifecycle modes.
+ *
+ * @see #PER_METHOD
+ * @see #PER_CLASS
*/
enum Lifecycle {
diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/Constants.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/Constants.java
index 34fc8d446a70..c78ebf4ee382 100644
--- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/Constants.java
+++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/Constants.java
@@ -70,6 +70,21 @@ public final class Constants {
*/
public static final String EXTENSIONS_AUTODETECTION_ENABLED_PROPERTY_NAME = "junit.jupiter.extensions.autodetection.enabled";
+ /**
+ * Property name used to set the default test instance lifecycle mode: {@value}
+ *
+ *
Supported Values
+ *
+ * Supported values include names of enum constants defined in
+ * {@link org.junit.jupiter.api.TestInstance.Lifecycle}, ignoring case.
+ *
+ *
If not specified, the default is "per_method" which corresponds to
+ * {@code @TestInstance(Lifecycle.PER_METHOD)}.
+ *
+ * @see org.junit.jupiter.api.TestInstance
+ */
+ public static final String DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME = "junit.jupiter.testinstance.lifecycle.default";
+
private Constants() {
/* no-op */
}
diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassTestDescriptor.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassTestDescriptor.java
index 977aba296d44..e4f3fa9e572d 100644
--- a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassTestDescriptor.java
+++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassTestDescriptor.java
@@ -14,6 +14,7 @@
import static org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.findAfterEachMethods;
import static org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.findBeforeAllMethods;
import static org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.findBeforeEachMethods;
+import static org.junit.jupiter.engine.descriptor.TestInstanceLifecycleUtils.getTestInstanceLifecycle;
import static org.junit.platform.commons.meta.API.Usage.Internal;
import java.lang.reflect.Constructor;
@@ -24,7 +25,6 @@
import java.util.Set;
import java.util.function.Function;
-import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
@@ -40,7 +40,6 @@
import org.junit.jupiter.engine.extension.ExtensionRegistry;
import org.junit.platform.commons.JUnitException;
import org.junit.platform.commons.meta.API;
-import org.junit.platform.commons.util.AnnotationUtils;
import org.junit.platform.commons.util.Preconditions;
import org.junit.platform.commons.util.ReflectionUtils;
import org.junit.platform.engine.TestDescriptor;
@@ -65,7 +64,6 @@ public class ClassTestDescriptor extends JupiterTestDescriptor {
private static final ExecutableInvoker executableInvoker = new ExecutableInvoker();
private final Class> testClass;
- private final Lifecycle lifecycle;
private List beforeAllMethods;
private List afterAllMethods;
@@ -83,7 +81,6 @@ protected ClassTestDescriptor(UniqueId uniqueId, Function, String> defa
defaultDisplayNameGenerator), new ClassSource(testClass));
this.testClass = testClass;
- this.lifecycle = getTestInstanceLifecycle(testClass);
}
// --- TestDescriptor ------------------------------------------------------
@@ -117,8 +114,10 @@ private static String generateDefaultDisplayName(Class> testClass) {
@Override
public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext context) {
- this.beforeAllMethods = findBeforeAllMethods(testClass, this.lifecycle == Lifecycle.PER_METHOD);
- this.afterAllMethods = findAfterAllMethods(testClass, this.lifecycle == Lifecycle.PER_METHOD);
+ Lifecycle lifecycle = getTestInstanceLifecycle(testClass, context.getConfigurationParameters());
+
+ this.beforeAllMethods = findBeforeAllMethods(testClass, lifecycle == Lifecycle.PER_METHOD);
+ this.afterAllMethods = findAfterAllMethods(testClass, lifecycle == Lifecycle.PER_METHOD);
this.beforeEachMethods = findBeforeEachMethods(testClass);
this.afterEachMethods = findAfterEachMethods(testClass);
@@ -134,7 +133,7 @@ public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext conte
// @formatter:off
return context.extend()
- .withTestInstanceProvider(testInstanceProvider(context, registry, extensionContext))
+ .withTestInstanceProvider(testInstanceProvider(context, registry, extensionContext, lifecycle))
.withExtensionRegistry(registry)
.withExtensionContext(extensionContext)
.withThrowableCollector(throwableCollector)
@@ -168,9 +167,9 @@ public void after(JupiterEngineExecutionContext context) throws Exception {
}
private TestInstanceProvider testInstanceProvider(JupiterEngineExecutionContext parentExecutionContext,
- ExtensionRegistry registry, ClassExtensionContext extensionContext) {
+ ExtensionRegistry registry, ClassExtensionContext extensionContext, Lifecycle lifecycle) {
- if (this.lifecycle == Lifecycle.PER_CLASS) {
+ if (lifecycle == Lifecycle.PER_CLASS) {
// Eagerly load test instance for BeforeAllCallbacks, if necessary,
// and store the instance in the ExtensionContext.
Object instance = instantiateAndPostProcessTestInstance(parentExecutionContext, extensionContext, registry);
@@ -283,7 +282,6 @@ private AfterEachMethodAdapter synthesizeAfterEachMethodAdapter(Method method) {
}
private void invokeMethodInExtensionContext(Method method, ExtensionContext context, ExtensionRegistry registry) {
-
Object testInstance = context.getRequiredTestInstance();
testInstance = ReflectionUtils.getOutermostInstance(testInstance, method.getDeclaringClass()).orElseThrow(
() -> new JUnitException("Failed to find instance for method: " + method.toGenericString()));
@@ -291,12 +289,4 @@ private void invokeMethodInExtensionContext(Method method, ExtensionContext cont
executableInvoker.invoke(method, testInstance, context, registry);
}
- private static TestInstance.Lifecycle getTestInstanceLifecycle(Class> testClass) {
- // @formatter:off
- return AnnotationUtils.findAnnotation(testClass, TestInstance.class)
- .map(TestInstance::value)
- .orElse(Lifecycle.PER_METHOD);
- // @formatter:on
- }
-
}
diff --git a/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestInstanceLifecycleUtils.java b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestInstanceLifecycleUtils.java
new file mode 100644
index 000000000000..8e0a23fca789
--- /dev/null
+++ b/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestInstanceLifecycleUtils.java
@@ -0,0 +1,83 @@
+/*
+ * 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.descriptor;
+
+import static java.util.logging.Level.WARNING;
+import static org.junit.jupiter.engine.Constants.DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME;
+
+import java.util.Optional;
+import java.util.logging.Logger;
+
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestInstance.Lifecycle;
+import org.junit.platform.commons.util.AnnotationUtils;
+import org.junit.platform.commons.util.Preconditions;
+import org.junit.platform.engine.ConfigurationParameters;
+
+/**
+ * Collection of utilities for retrieving the test instance lifecycle mode.
+ *
+ * @since 5.0
+ * @see TestInstance
+ * @see TestInstance.Lifecycle
+ */
+final class TestInstanceLifecycleUtils {
+
+ private static final Logger LOG = Logger.getLogger(TestInstanceLifecycleUtils.class.getName());
+
+ ///CLOVER:OFF
+ private TestInstanceLifecycleUtils() {
+ /* no-op */
+ }
+ ///CLOVER:ON
+
+ static TestInstance.Lifecycle getTestInstanceLifecycle(Class> testClass, ConfigurationParameters configParams) {
+ Preconditions.notNull(testClass, "testClass must not be null");
+ Preconditions.notNull(configParams, "ConfigurationParameters must not be null");
+
+ // @formatter:off
+ return AnnotationUtils.findAnnotation(testClass, TestInstance.class)
+ .map(TestInstance::value)
+ .orElseGet(() -> getDefaultTestInstanceLifecycle(configParams));
+ // @formatter:on
+ }
+
+ // TODO Consider looking up the default test instance lifecycle mode once per test plan execution.
+ static TestInstance.Lifecycle getDefaultTestInstanceLifecycle(ConfigurationParameters configParams) {
+ Preconditions.notNull(configParams, "ConfigurationParameters must not be null");
+ String propertyName = DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME;
+
+ Optional optional = configParams.get(propertyName);
+ String constantName = null;
+ if (optional.isPresent()) {
+ try {
+ constantName = optional.get().trim().toUpperCase();
+ Lifecycle lifecycle = TestInstance.Lifecycle.valueOf(constantName);
+ LOG.info(() -> String.format(
+ "Using default test instance lifecycle mode '%s' set via the '%s' configuration parameter.",
+ lifecycle, propertyName));
+ return lifecycle;
+ }
+ catch (Exception ex) {
+ // local copy necessary for use in lambda expression
+ String constant = constantName;
+ LOG.log(WARNING, ex,
+ () -> String.format(
+ "Invalid test instance lifecycle mode '%s' set via the '%s' configuration parameter. "
+ + "Falling back to %s lifecycle semantics.",
+ constant, propertyName, Lifecycle.PER_METHOD.name()));
+ }
+ }
+
+ return Lifecycle.PER_METHOD;
+ }
+
+}
diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleConfigurationTests.java b/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleConfigurationTests.java
new file mode 100644
index 000000000000..cb71bfea888d
--- /dev/null
+++ b/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleConfigurationTests.java
@@ -0,0 +1,237 @@
+/*
+ * 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.Collections.emptyMap;
+import static java.util.Collections.singletonMap;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
+import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_METHOD;
+import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
+import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+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.TestInstance;
+import org.junit.platform.engine.ConfigurationParameters;
+import org.junit.platform.engine.test.event.ExecutionEventRecorder;
+import org.junit.platform.launcher.Launcher;
+
+/**
+ * Integration tests for {@link TestInstance @TestInstance} lifecycle
+ * configuration support, not to be confused with {@link TestInstanceLifecycleTests}.
+ *
+ * Specifically, this class tests custom lifecycle configuration specified
+ * via {@code @TestInstance} as well as via {@link ConfigurationParameters}
+ * supplied to the {@link Launcher} or via a JVM system property.
+ *
+ * @since 5.0
+ * @see TestInstanceLifecycleTests
+ */
+class TestInstanceLifecycleConfigurationTests extends AbstractJupiterTestEngineTests {
+
+ private static final String KEY = Constants.DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME;
+
+ private static final List methodsInvoked = new ArrayList<>();
+
+ @BeforeEach
+ @AfterEach
+ void reset() {
+ methodsInvoked.clear();
+ System.clearProperty(KEY);
+ }
+
+ @Test
+ void instancePerMethodUsingStandardDefaultConfiguration() {
+ performAssertions(AssumedInstancePerTestMethodTestCase.class, 2, 0, 1, "beforeAll", "test", "afterAll");
+ }
+
+ @Test
+ void instancePerClassConfiguredViaExplicitAnnotationDeclaration() {
+ performAssertions(ExplicitInstancePerClassTestCase.class, 2, 0, 1, "beforeAll", "test", "afterAll");
+ }
+
+ @Test
+ void instancePerClassConfiguredViaSystemProperty() {
+ Class> testClass = AssumedInstancePerClassTestCase.class;
+
+ // Should fail by default...
+ performAssertions(testClass, 2, 1, 0);
+
+ // Should pass with the system property set
+ System.setProperty(KEY, PER_CLASS.name());
+ performAssertions(testClass, 2, 0, 1, "beforeAll", "test", "afterAll");
+ }
+
+ @Test
+ void instancePerClassConfiguredViaConfigParam() {
+ Class> testClass = AssumedInstancePerClassTestCase.class;
+
+ // Should fail by default...
+ performAssertions(testClass, 2, 1, 0);
+
+ // Should pass with the config param
+ performAssertions(testClass, singletonMap(KEY, PER_CLASS.name()), 2, 0, 1, "beforeAll", "test", "afterAll");
+ }
+
+ @Test
+ void instancePerClassConfiguredViaConfigParamThatOverridesSystemProperty() {
+ Class> testClass = AssumedInstancePerClassTestCase.class;
+
+ // Should fail with system property
+ System.setProperty(KEY, PER_METHOD.name());
+ performAssertions(testClass, 2, 1, 0);
+
+ // Should pass with the config param
+ performAssertions(testClass, singletonMap(KEY, PER_CLASS.name()), 2, 0, 1, "beforeAll", "test", "afterAll");
+ }
+
+ @Test
+ void instancePerMethodConfiguredViaExplicitAnnotationDeclarationThatOverridesSystemProperty() {
+ System.setProperty(KEY, PER_CLASS.name());
+ performAssertions(ExplicitInstancePerTestMethodTestCase.class, 2, 0, 1, "beforeAll", "test", "afterAll");
+ }
+
+ @Test
+ void instancePerMethodConfiguredViaExplicitAnnotationDeclarationThatOverridesConfigParam() {
+ Class> testClass = ExplicitInstancePerTestMethodTestCase.class;
+ performAssertions(testClass, singletonMap(KEY, PER_CLASS.name()), 2, 0, 1, "beforeAll", "test", "afterAll");
+ }
+
+ private void performAssertions(Class> testClass, int containers, int containersFailed, int tests,
+ String... methods) {
+
+ performAssertions(testClass, emptyMap(), containers, containersFailed, tests, methods);
+ }
+
+ private void performAssertions(Class> testClass, Map configParams, int containers,
+ int failedContainers, int tests, String... methods) {
+
+ // @formatter:off
+ ExecutionEventRecorder eventRecorder = executeTests(
+ request()
+ .selectors(selectClass(testClass))
+ .configurationParameters(configParams)
+ .build()
+ );
+
+ assertAll(
+ () -> assertEquals(containers, eventRecorder.getContainerStartedCount(), "# containers started"),
+ () -> assertEquals(containers, eventRecorder.getContainerFinishedCount(), "# containers finished"),
+ () -> assertEquals(failedContainers, eventRecorder.getContainerFailedCount(), "# containers failed"),
+ () -> assertEquals(tests, eventRecorder.getTestStartedCount(), "# tests started"),
+ () -> assertEquals(tests, eventRecorder.getTestSuccessfulCount(), "# tests succeeded"),
+ () -> assertEquals(Arrays.asList(methods), methodsInvoked)
+ );
+ // @formatter:on
+ }
+
+ // -------------------------------------------------------------------------
+
+ @TestInstance(PER_METHOD)
+ private static class ExplicitInstancePerTestMethodTestCase {
+
+ @BeforeAll
+ static void beforeAll() {
+ methodsInvoked.add("beforeAll");
+ }
+
+ @Test
+ void test() {
+ methodsInvoked.add("test");
+ }
+
+ @AfterAll
+ static void afterAllStatic() {
+ methodsInvoked.add("afterAll");
+ }
+
+ }
+
+ /**
+ * "per-method" lifecycle mode is assumed since the {@code @BeforeAll} and
+ * {@code @AfterAll} methods are static, even though there is no explicit
+ * {@code @TestInstance} declaration.
+ */
+ private static class AssumedInstancePerTestMethodTestCase {
+
+ @BeforeAll
+ static void beforeAll() {
+ methodsInvoked.add("beforeAll");
+ }
+
+ @Test
+ void test() {
+ methodsInvoked.add("test");
+ }
+
+ @AfterAll
+ static void afterAllStatic() {
+ methodsInvoked.add("afterAll");
+ }
+
+ }
+
+ @TestInstance(PER_CLASS)
+ private static class ExplicitInstancePerClassTestCase {
+
+ @BeforeAll
+ void beforeAll(TestInfo testInfo) {
+ methodsInvoked.add("beforeAll");
+ }
+
+ @Test
+ void test() {
+ methodsInvoked.add("test");
+ }
+
+ @AfterAll
+ void afterAll(TestInfo testInfo) {
+ methodsInvoked.add("afterAll");
+ }
+
+ }
+
+ /**
+ * "per-class" lifecycle mode is assumed since the {@code @BeforeAll} and
+ * {@code @AfterAll} methods are non-static, even though there is no
+ * explicit {@code @TestInstance} declaration.
+ */
+ private static class AssumedInstancePerClassTestCase {
+
+ @BeforeAll
+ void beforeAll(TestInfo testInfo) {
+ methodsInvoked.add("beforeAll");
+ }
+
+ @Test
+ void test() {
+ methodsInvoked.add("test");
+ }
+
+ @AfterAll
+ void afterAll(TestInfo testInfo) {
+ methodsInvoked.add("afterAll");
+ }
+
+ }
+
+}
diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleTests.java b/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleTests.java
index 994adcb598ce..bc430c81b997 100644
--- a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleTests.java
+++ b/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleTests.java
@@ -63,6 +63,7 @@
* Integration tests for {@link TestInstance @TestInstance} lifecycle support.
*
* @since 5.0
+ * @see TestInstanceLifecycleConfigurationTests
*/
class TestInstanceLifecycleTests extends AbstractJupiterTestEngineTests {
diff --git a/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/TestInstanceLifecycleUtilsTests.java b/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/TestInstanceLifecycleUtilsTests.java
new file mode 100644
index 000000000000..90f6ccdb65bd
--- /dev/null
+++ b/junit-jupiter-engine/src/test/java/org/junit/jupiter/engine/descriptor/TestInstanceLifecycleUtilsTests.java
@@ -0,0 +1,118 @@
+/*
+ * 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.descriptor;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
+import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_METHOD;
+import static org.junit.jupiter.engine.Constants.DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME;
+import static org.junit.jupiter.engine.descriptor.TestInstanceLifecycleUtils.getDefaultTestInstanceLifecycle;
+import static org.junit.jupiter.engine.descriptor.TestInstanceLifecycleUtils.getTestInstanceLifecycle;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Optional;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestInstance.Lifecycle;
+import org.junit.platform.commons.util.PreconditionViolationException;
+import org.junit.platform.engine.ConfigurationParameters;
+
+/**
+ * Unit tests for {@link TestInstanceLifecycleUtils}.
+ *
+ * NOTE: it doesn't make sense to unit test the JVM system property fallback
+ * support in this test class since that feature is a concrete implementation
+ * detail of {@code LauncherConfigurationParameters} which necessitates an
+ * integration test via the {@code Launcher} API.
+ *
+ * @since 5.0
+ */
+class TestInstanceLifecycleUtilsTests {
+
+ private static final String KEY = DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME;
+
+ @Test
+ void getDefaultTestInstanceLifecyclePreconditions() {
+ PreconditionViolationException exception = assertThrows(PreconditionViolationException.class,
+ () -> getDefaultTestInstanceLifecycle(null));
+ assertThat(exception).hasMessage("ConfigurationParameters must not be null");
+ }
+
+ @Test
+ void getDefaultTestInstanceLifecycleWithNoConfigParamSet() {
+ Lifecycle lifecycle = getDefaultTestInstanceLifecycle(mock(ConfigurationParameters.class));
+ assertThat(lifecycle).isEqualTo(PER_METHOD);
+ }
+
+ @Test
+ void getDefaultTestInstanceLifecycleWithConfigParamSet() {
+ assertAll(//
+ () -> assertDefaultConfigParam(null, PER_METHOD), //
+ () -> assertDefaultConfigParam("", PER_METHOD), //
+ () -> assertDefaultConfigParam("bogus", PER_METHOD), //
+ () -> assertDefaultConfigParam(PER_METHOD.name(), PER_METHOD), //
+ () -> assertDefaultConfigParam(PER_METHOD.name().toLowerCase(), PER_METHOD), //
+ () -> assertDefaultConfigParam(" " + PER_METHOD.name() + " ", PER_METHOD), //
+ () -> assertDefaultConfigParam(PER_CLASS.name(), PER_CLASS), //
+ () -> assertDefaultConfigParam(PER_CLASS.name().toLowerCase(), PER_CLASS), //
+ () -> assertDefaultConfigParam(" " + PER_CLASS.name() + " ", Lifecycle.PER_CLASS) //
+ );
+ }
+
+ private void assertDefaultConfigParam(String configValue, Lifecycle expected) {
+ ConfigurationParameters configParams = mock(ConfigurationParameters.class);
+ when(configParams.get(KEY)).thenReturn(Optional.ofNullable(configValue));
+ Lifecycle lifecycle = getDefaultTestInstanceLifecycle(configParams);
+ assertThat(lifecycle).isEqualTo(expected);
+ }
+
+ @Test
+ void getTestInstanceLifecyclePreconditions() {
+ PreconditionViolationException exception = assertThrows(PreconditionViolationException.class,
+ () -> getTestInstanceLifecycle(null, mock(ConfigurationParameters.class)));
+ assertThat(exception).hasMessage("testClass must not be null");
+
+ exception = assertThrows(PreconditionViolationException.class,
+ () -> getTestInstanceLifecycle(getClass(), null));
+ assertThat(exception).hasMessage("ConfigurationParameters must not be null");
+ }
+
+ @Test
+ void getTestInstanceLifecycleWithNoConfigParamSet() {
+ Lifecycle lifecycle = getTestInstanceLifecycle(getClass(), mock(ConfigurationParameters.class));
+ assertThat(lifecycle).isEqualTo(PER_METHOD);
+ }
+
+ @Test
+ void getTestInstanceLifecycleWithConfigParamSet() {
+ ConfigurationParameters configParams = mock(ConfigurationParameters.class);
+ when(configParams.get(KEY)).thenReturn(Optional.of(PER_CLASS.name().toLowerCase()));
+ Lifecycle lifecycle = getTestInstanceLifecycle(getClass(), configParams);
+ assertThat(lifecycle).isEqualTo(PER_CLASS);
+ }
+
+ @Test
+ void getTestInstanceLifecycleWithLocalConfigThatOverridesCustomDefaultSetViaConfigParam() {
+ ConfigurationParameters configParams = mock(ConfigurationParameters.class);
+ when(configParams.get(KEY)).thenReturn(Optional.of(PER_CLASS.name().toLowerCase()));
+ Lifecycle lifecycle = getTestInstanceLifecycle(TestCase.class, configParams);
+ assertThat(lifecycle).isEqualTo(PER_METHOD);
+ }
+
+ @TestInstance(Lifecycle.PER_METHOD)
+ private static class TestCase {
+ }
+
+}