Skip to content

Commit e477e83

Browse files
committed
Introduce configuration parameter for default extension context scope
1 parent a506608 commit e477e83

File tree

9 files changed

+106
-22
lines changed

9 files changed

+106
-22
lines changed

junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstantiationAwareExtension.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,16 @@ enum ExtensionContextScope {
106106

107107
/**
108108
* The extension should receive an {@link ExtensionContext} scoped to
109-
* the test class.
109+
* in the <em>default</em> scope.
110+
*
111+
* <p>The default scope is determined by the configuration parameter
112+
* {@link #DEFAULT_SCOPE_PROPERTY_NAME}. If not specified, extensions
113+
* will receive an {@link ExtensionContext} scoped to the test class.
110114
*
111115
* @deprecated This behavior will be removed from future versions of
112116
* JUnit and {@link #TEST_METHOD} will become the default.
117+
*
118+
* @see #DEFAULT_SCOPE_PROPERTY_NAME
113119
*/
114120
@API(status = DEPRECATED, since = "5.12") //
115121
@Deprecated
@@ -120,7 +126,19 @@ enum ExtensionContextScope {
120126
* the test method, unless the
121127
* {@link TestInstance.Lifecycle#PER_CLASS PER_CLASS} lifecycle is used.
122128
*/
123-
TEST_METHOD
129+
TEST_METHOD;
130+
131+
/**
132+
* Property name used to set the default extension context scope: {@value}
133+
*
134+
* <h4>Supported Values</h4>
135+
*
136+
* <p>Supported values include names of enum constants defined in this
137+
* class, ignoring case.
138+
*
139+
* @see #DEFAULT
140+
*/
141+
public static final String DEFAULT_SCOPE_PROPERTY_NAME = "junit.jupiter.extensions.testInstantiation.extensionContextScope.default";
124142
}
125143

126144
}

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/Constants.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.junit.jupiter.api.TestInstance;
3434
import org.junit.jupiter.api.TestTemplate;
3535
import org.junit.jupiter.api.Timeout;
36+
import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope;
3637
import org.junit.jupiter.api.io.TempDir;
3738
import org.junit.jupiter.api.parallel.Execution;
3839
import org.junit.jupiter.engine.config.JupiterConfiguration;
@@ -369,6 +370,16 @@ public final class Constants {
369370
@API(status = EXPERIMENTAL, since = "5.10")
370371
public static final String DEFAULT_TEMP_DIR_FACTORY_PROPERTY_NAME = TempDir.DEFAULT_FACTORY_PROPERTY_NAME;
371372

373+
/**
374+
* Property name used to set the default extension context scope for
375+
* extensions that participate in test instantiation: {@value}
376+
*
377+
* @since 5.12
378+
* @see org.junit.jupiter.api.extension.TestInstantiationAwareExtension
379+
*/
380+
@API(status = EXPERIMENTAL, since = "5.12")
381+
public static final String DEFAULT_TEST_CLASS_INSTANCE_CONSTRUCTION_EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME = ExtensionContextScope.DEFAULT_SCOPE_PROPERTY_NAME;
382+
372383
private Constants() {
373384
/* no-op */
374385
}

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/CachingJupiterConfiguration.java

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.junit.jupiter.api.MethodOrderer;
2727
import org.junit.jupiter.api.TestInstance;
2828
import org.junit.jupiter.api.extension.ExecutionCondition;
29+
import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope;
2930
import org.junit.jupiter.api.io.CleanupMode;
3031
import org.junit.jupiter.api.io.TempDirFactory;
3132
import org.junit.jupiter.api.parallel.ExecutionMode;
@@ -58,71 +59,77 @@ public <T> Optional<T> getRawConfigurationParameter(String key, Function<String,
5859
@Override
5960
public boolean isParallelExecutionEnabled() {
6061
return (boolean) cache.computeIfAbsent(PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME,
61-
key -> delegate.isParallelExecutionEnabled());
62+
__ -> delegate.isParallelExecutionEnabled());
6263
}
6364

6465
@Override
6566
public boolean isExtensionAutoDetectionEnabled() {
6667
return (boolean) cache.computeIfAbsent(EXTENSIONS_AUTODETECTION_ENABLED_PROPERTY_NAME,
67-
key -> delegate.isExtensionAutoDetectionEnabled());
68+
__ -> delegate.isExtensionAutoDetectionEnabled());
6869
}
6970

7071
@Override
7172
public ExecutionMode getDefaultExecutionMode() {
7273
return (ExecutionMode) cache.computeIfAbsent(DEFAULT_EXECUTION_MODE_PROPERTY_NAME,
73-
key -> delegate.getDefaultExecutionMode());
74+
__ -> delegate.getDefaultExecutionMode());
7475
}
7576

7677
@Override
7778
public ExecutionMode getDefaultClassesExecutionMode() {
7879
return (ExecutionMode) cache.computeIfAbsent(DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME,
79-
key -> delegate.getDefaultClassesExecutionMode());
80+
__ -> delegate.getDefaultClassesExecutionMode());
8081
}
8182

8283
@Override
8384
public TestInstance.Lifecycle getDefaultTestInstanceLifecycle() {
8485
return (TestInstance.Lifecycle) cache.computeIfAbsent(DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME,
85-
key -> delegate.getDefaultTestInstanceLifecycle());
86+
__ -> delegate.getDefaultTestInstanceLifecycle());
8687
}
8788

8889
@SuppressWarnings("unchecked")
8990
@Override
9091
public Predicate<ExecutionCondition> getExecutionConditionFilter() {
9192
return (Predicate<ExecutionCondition>) cache.computeIfAbsent(DEACTIVATE_CONDITIONS_PATTERN_PROPERTY_NAME,
92-
key -> delegate.getExecutionConditionFilter());
93+
__ -> delegate.getExecutionConditionFilter());
9394
}
9495

9596
@Override
9697
public DisplayNameGenerator getDefaultDisplayNameGenerator() {
9798
return (DisplayNameGenerator) cache.computeIfAbsent(DEFAULT_DISPLAY_NAME_GENERATOR_PROPERTY_NAME,
98-
key -> delegate.getDefaultDisplayNameGenerator());
99+
__ -> delegate.getDefaultDisplayNameGenerator());
99100
}
100101

101102
@SuppressWarnings("unchecked")
102103
@Override
103104
public Optional<MethodOrderer> getDefaultTestMethodOrderer() {
104105
return (Optional<MethodOrderer>) cache.computeIfAbsent(DEFAULT_TEST_METHOD_ORDER_PROPERTY_NAME,
105-
key -> delegate.getDefaultTestMethodOrderer());
106+
__ -> delegate.getDefaultTestMethodOrderer());
106107
}
107108

108109
@SuppressWarnings("unchecked")
109110
@Override
110111
public Optional<ClassOrderer> getDefaultTestClassOrderer() {
111112
return (Optional<ClassOrderer>) cache.computeIfAbsent(DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME,
112-
key -> delegate.getDefaultTestClassOrderer());
113+
__ -> delegate.getDefaultTestClassOrderer());
113114
}
114115

115116
@Override
116117
public CleanupMode getDefaultTempDirCleanupMode() {
117118
return (CleanupMode) cache.computeIfAbsent(DEFAULT_CLEANUP_MODE_PROPERTY_NAME,
118-
key -> delegate.getDefaultTempDirCleanupMode());
119+
__ -> delegate.getDefaultTempDirCleanupMode());
119120
}
120121

121122
@SuppressWarnings("unchecked")
122123
@Override
123124
public Supplier<TempDirFactory> getDefaultTempDirFactorySupplier() {
124125
return (Supplier<TempDirFactory>) cache.computeIfAbsent(DEFAULT_FACTORY_PROPERTY_NAME,
125-
key -> delegate.getDefaultTempDirFactorySupplier());
126+
__ -> delegate.getDefaultTempDirFactorySupplier());
126127
}
127128

129+
@Override
130+
public ExtensionContextScope getDefaultTestInstantiationExtensionContextScope() {
131+
return (ExtensionContextScope) cache.computeIfAbsent(
132+
DEFAULT_TEST_INSTANTIATION_EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME,
133+
__ -> delegate.getDefaultTestInstantiationExtensionContextScope());
134+
}
128135
}

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/DefaultJupiterConfiguration.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.junit.jupiter.api.MethodOrderer;
2727
import org.junit.jupiter.api.TestInstance.Lifecycle;
2828
import org.junit.jupiter.api.extension.ExecutionCondition;
29+
import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope;
2930
import org.junit.jupiter.api.io.CleanupMode;
3031
import org.junit.jupiter.api.io.TempDirFactory;
3132
import org.junit.jupiter.api.parallel.ExecutionMode;
@@ -62,6 +63,9 @@ public class DefaultJupiterConfiguration implements JupiterConfiguration {
6263
private static final InstantiatingConfigurationParameterConverter<TempDirFactory> tempDirFactoryConverter = //
6364
new InstantiatingConfigurationParameterConverter<>(TempDirFactory.class, "temp dir factory");
6465

66+
private static final EnumConfigurationParameterConverter<ExtensionContextScope> extensionContextScopeConverter = //
67+
new EnumConfigurationParameterConverter<>(ExtensionContextScope.class, "extension context scope");
68+
6569
private final ConfigurationParameters configurationParameters;
6670

6771
public DefaultJupiterConfiguration(ConfigurationParameters configurationParameters) {
@@ -141,4 +145,10 @@ public Supplier<TempDirFactory> getDefaultTempDirFactorySupplier() {
141145
return () -> supplier.get().orElse(TempDirFactory.Standard.INSTANCE);
142146
}
143147

148+
@SuppressWarnings("deprecation")
149+
@Override
150+
public ExtensionContextScope getDefaultTestInstantiationExtensionContextScope() {
151+
return extensionContextScopeConverter.get(configurationParameters,
152+
DEFAULT_TEST_INSTANTIATION_EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME, ExtensionContextScope.DEFAULT);
153+
}
144154
}

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/JupiterConfiguration.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.junit.jupiter.api.MethodOrderer;
2424
import org.junit.jupiter.api.TestInstance;
2525
import org.junit.jupiter.api.extension.ExecutionCondition;
26+
import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope;
2627
import org.junit.jupiter.api.io.CleanupMode;
2728
import org.junit.jupiter.api.io.TempDirFactory;
2829
import org.junit.jupiter.api.parallel.Execution;
@@ -42,7 +43,8 @@ public interface JupiterConfiguration {
4243
String DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME = TestInstance.Lifecycle.DEFAULT_LIFECYCLE_PROPERTY_NAME;
4344
String DEFAULT_DISPLAY_NAME_GENERATOR_PROPERTY_NAME = DisplayNameGenerator.DEFAULT_GENERATOR_PROPERTY_NAME;
4445
String DEFAULT_TEST_METHOD_ORDER_PROPERTY_NAME = MethodOrderer.DEFAULT_ORDER_PROPERTY_NAME;
45-
String DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME = ClassOrderer.DEFAULT_ORDER_PROPERTY_NAME;
46+
String DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME = ClassOrderer.DEFAULT_ORDER_PROPERTY_NAME;;
47+
String DEFAULT_TEST_INSTANTIATION_EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME = ExtensionContextScope.DEFAULT_SCOPE_PROPERTY_NAME;
4648

4749
Optional<String> getRawConfigurationParameter(String key);
4850

@@ -70,4 +72,6 @@ public interface JupiterConfiguration {
7072

7173
Supplier<TempDirFactory> getDefaultTempDirFactorySupplier();
7274

75+
ExtensionContextScope getDefaultTestInstantiationExtensionContextScope();
76+
7377
}

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassBasedTestDescriptor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ private TestInstances instantiateAndPostProcessTestInstance(JupiterEngineExecuti
288288
JupiterEngineExecutionContext context) {
289289

290290
ExtensionContextSupplier extensionContext = ExtensionContextSupplier.create(context.getExtensionContext(),
291-
ourExtensionContext);
291+
ourExtensionContext, configuration);
292292
TestInstances instances = instantiateTestClass(parentExecutionContext, extensionContext, registry, context);
293293
context.getThrowableCollector().execute(() -> {
294294
invokeTestInstancePostProcessors(instances.getInnermostInstance(), registry, extensionContext);

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ExtensionContextSupplier.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.apiguardian.api.API;
1717
import org.junit.jupiter.api.extension.ExtensionContext;
1818
import org.junit.jupiter.api.extension.TestInstantiationAwareExtension;
19+
import org.junit.jupiter.engine.config.JupiterConfiguration;
1920

2021
/**
2122
* Container of two instances of {@link ExtensionContext} to simplify the legacy for
@@ -29,8 +30,9 @@
2930
public interface ExtensionContextSupplier {
3031

3132
static ExtensionContextSupplier create(ExtensionContext currentExtensionContext,
32-
ExtensionContext legacyExtensionContext) {
33-
if (currentExtensionContext == legacyExtensionContext) {
33+
ExtensionContext legacyExtensionContext, JupiterConfiguration configuration) {
34+
if (currentExtensionContext == legacyExtensionContext
35+
|| configuration.getDefaultTestInstantiationExtensionContextScope() == TEST_METHOD) {
3436
return __ -> currentExtensionContext;
3537
}
3638
return new ScopeBasedExtensionContextSupplier(currentExtensionContext, legacyExtensionContext);

jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/InterceptingExecutableInvokerTests.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,8 @@ void invokeMethod() {
3131

3232
@Override
3333
<T> T invokeConstructor(Constructor<T> constructor, Object outerInstance) {
34-
ExtensionContextSupplier context = ExtensionContextSupplier.create(extensionContext, extensionContext);
35-
return newInvoker().invoke(constructor, Optional.ofNullable(outerInstance), context, extensionRegistry,
36-
passthroughInterceptor());
34+
return newInvoker().invoke(constructor, Optional.ofNullable(outerInstance), __ -> extensionContext,
35+
extensionRegistry, passthroughInterceptor());
3736
}
3837

3938
private InterceptingExecutableInvoker newInvoker() {

jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstanceFactoryTests.java

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
1616
import static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.TEST_METHOD;
1717
import static org.junit.platform.commons.util.ClassUtils.nullSafeToString;
18+
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
19+
import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;
1820
import static org.junit.platform.testkit.engine.EventConditions.container;
1921
import static org.junit.platform.testkit.engine.EventConditions.engine;
2022
import static org.junit.platform.testkit.engine.EventConditions.event;
@@ -44,6 +46,7 @@
4446
import org.junit.jupiter.api.extension.TestInstanceFactoryContext;
4547
import org.junit.jupiter.api.extension.TestInstantiationException;
4648
import org.junit.jupiter.engine.AbstractJupiterTestEngineTests;
49+
import org.junit.jupiter.engine.Constants;
4750
import org.junit.platform.commons.support.ReflectionSupport;
4851
import org.junit.platform.commons.test.TestClassLoader;
4952
import org.junit.platform.testkit.engine.EngineExecutionResults;
@@ -429,6 +432,36 @@ void instanceFactoryWithLegacyContext() {
429432
// @formatter:on
430433
}
431434

435+
@Test
436+
void instanceFactoryWithLegacyContextAndChangedDefaultScope() {
437+
var executionResults = executeTests(request() //
438+
.selectors(selectClass(LegacyContextTestCase.class)) //
439+
.configurationParameter(
440+
Constants.DEFAULT_TEST_CLASS_INSTANCE_CONSTRUCTION_EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME,
441+
TEST_METHOD.name()));
442+
443+
assertEquals(3, executionResults.testEvents().started().count(), "# tests started");
444+
assertEquals(3, executionResults.testEvents().succeeded().count(), "# tests succeeded");
445+
446+
// @formatter:off
447+
assertThat(callSequence).containsExactly(
448+
"LegacyInstanceFactory instantiated: LegacyContextTestCase",
449+
"outerTest",
450+
"close LegacyContextTestCase",
451+
"LegacyInstanceFactory instantiated: LegacyContextTestCase",
452+
"LegacyInstanceFactory instantiated: InnerTestCase",
453+
"innerTest1",
454+
"close InnerTestCase",
455+
"close LegacyContextTestCase",
456+
"LegacyInstanceFactory instantiated: LegacyContextTestCase",
457+
"LegacyInstanceFactory instantiated: InnerTestCase",
458+
"innerTest2",
459+
"close InnerTestCase",
460+
"close LegacyContextTestCase"
461+
);
462+
// @formatter:on
463+
}
464+
432465
// -------------------------------------------------------------------------
433466

434467
@SuppressWarnings("JUnitMalformedDeclaration")
@@ -797,8 +830,8 @@ public Object createTestInstance(TestInstanceFactoryContext factoryContext, Exte
797830
}
798831
}
799832

800-
private static boolean instantiated(Class<? extends TestInstanceFactory> factoryClass, Class<?> testClass) {
801-
return callSequence.add(factoryClass.getSimpleName() + " instantiated: " + testClass.getSimpleName());
833+
private static void instantiated(Class<? extends TestInstanceFactory> factoryClass, Class<?> testClass) {
834+
callSequence.add(factoryClass.getSimpleName() + " instantiated: " + testClass.getSimpleName());
802835
}
803836

804837
}

0 commit comments

Comments
 (0)