diff --git a/spring-test/src/main/java/org/springframework/test/context/RetainApplicationContext.java b/spring-test/src/main/java/org/springframework/test/context/RetainApplicationContext.java new file mode 100644 index 000000000000..8d79888e7b5d --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/RetainApplicationContext.java @@ -0,0 +1,28 @@ +package org.springframework.test.context; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that the {@link org.springframework.context.ApplicationContext} + * associated with the test should be retained between test classes and + * not paused when it is reused from the context cache. + * + *

This is an opt-in mechanism intended for tests that rely on expensive + * {@link org.springframework.context.SmartLifecycle} components whose + * stop/start cycles significantly impact test performance. + * + *

When a test class is annotated with {@code @RetainApplicationContext}, + * the application context will not be paused or restarted between + * test classes, even if the context would normally be considered unused. + * + * @since 7.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface RetainApplicationContext { +} diff --git a/spring-test/src/main/java/org/springframework/test/context/support/DefaultTestContext.java b/spring-test/src/main/java/org/springframework/test/context/support/DefaultTestContext.java index 850c752dac27..e778b30693a2 100644 --- a/spring-test/src/main/java/org/springframework/test/context/support/DefaultTestContext.java +++ b/spring-test/src/main/java/org/springframework/test/context/support/DefaultTestContext.java @@ -25,14 +25,12 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.style.DefaultToStringStyler; import org.springframework.core.style.SimpleValueStyler; import org.springframework.core.style.ToStringCreator; import org.springframework.test.annotation.DirtiesContext.HierarchyMode; -import org.springframework.test.context.CacheAwareContextLoaderDelegate; -import org.springframework.test.context.MergedContextConfiguration; -import org.springframework.test.context.MethodInvoker; -import org.springframework.test.context.TestContext; +import org.springframework.test.context.*; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -145,16 +143,28 @@ public ApplicationContext getApplicationContext() { * with this test context as unused so that it can be safely * {@linkplain org.springframework.context.ConfigurableApplicationContext#pause() paused} * if no other test classes are actively using the same application context. + * + *

When a test class is annotated with {@link RetainApplicationContext}, the + * application context will not be marked as unused and will be retained + * between test classes. This is intended for tests that rely on expensive + * {@link org.springframework.context.SmartLifecycle} components whose stop/start + * cycles significantly impact test performance. + * *

The default implementation delegates to the {@link CacheAwareContextLoaderDelegate} * that was supplied when this {@code TestContext} was constructed. + * * @since 7.0 * @see CacheAwareContextLoaderDelegate#unregisterContextUsage(MergedContextConfiguration, Class) + * @see RetainApplicationContext */ @Override public void markApplicationContextUnused() { - this.cacheAwareContextLoaderDelegate.unregisterContextUsage(this.mergedConfig, this.testClass); + if (!AnnotatedElementUtils.hasAnnotation(this.testClass, RetainApplicationContext.class)) { + this.cacheAwareContextLoaderDelegate.unregisterContextUsage(this.mergedConfig, this.testClass); + } } + /** * Mark the {@linkplain ApplicationContext application context} associated * with this test context as dirty (i.e., by removing it from the