From 77b4fe6a1fd01ddd06e1d00f329d884551545d98 Mon Sep 17 00:00:00 2001 From: Painfu11y <333vahan777@gmail.com> Date: Tue, 23 Dec 2025 20:51:58 +0400 Subject: [PATCH] Retain application context between test classes Add @RetainApplicationContext annotation to prevent pausing and restarting the ApplicationContext between test classes when SmartLifecycle beans are expensive to stop/start. Update DefaultTestContext.markApplicationContextUnused() to respect this annotation. Closes gh-36044 Signed-off-by: Painfu11y <333vahan777@gmail.com> --- .../context/RetainApplicationContext.java | 28 +++++++++++++++++++ .../context/support/DefaultTestContext.java | 20 +++++++++---- 2 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 spring-test/src/main/java/org/springframework/test/context/RetainApplicationContext.java 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