diff --git a/spring-test/src/main/java/org/springframework/test/context/BootstrapContext.java b/spring-test/src/main/java/org/springframework/test/context/BootstrapContext.java index fa12a797e347006cebbb2bca1b968883c9a2cf06..19a7e89acecfc25aff8f4ebb02c4831f00289dee 100644 --- a/spring-test/src/main/java/org/springframework/test/context/BootstrapContext.java +++ b/spring-test/src/main/java/org/springframework/test/context/BootstrapContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,14 +28,15 @@ package org.springframework.test.context; public interface BootstrapContext { /** - * Get the {@link Class test class} for this bootstrap context. + * Get the {@linkplain Class test class} for this bootstrap context. * @return the test class (never {@code null}) */ Class> getTestClass(); /** * Get the {@link CacheAwareContextLoaderDelegate} to use for transparent - * interaction with the context cache. + * interaction with the {@code ContextCache}. + * @return the context loader delegate (never {@code null}) */ CacheAwareContextLoaderDelegate getCacheAwareContextLoaderDelegate(); diff --git a/spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java b/spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java index d0f9c07e8991d02b33ee24b3fe7327b3f0215b83..bf84a9a80e1729e6ef5ae87c46045faa59f13ecb 100644 --- a/spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java +++ b/spring-test/src/main/java/org/springframework/test/context/BootstrapUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ package org.springframework.test.context; +import java.lang.reflect.Constructor; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -36,6 +38,10 @@ import static org.springframework.core.annotation.AnnotationUtils.*; */ abstract class BootstrapUtils { + private static final String DEFAULT_BOOTSTRAP_CONTEXT_CLASS_NAME = "org.springframework.test.context.support.DefaultBootstrapContext"; + + private static final String DEFAULT_CACHE_AWARE_CONTEXT_LOADER_DELEGATE_CLASS_NAME = "org.springframework.test.context.support.DefaultCacheAwareContextLoaderDelegate"; + private static final String DEFAULT_TEST_CONTEXT_BOOTSTRAPPER_CLASS_NAME = "org.springframework.test.context.support.DefaultTestContextBootstrapper"; private static final Log logger = LogFactory.getLog(BootstrapUtils.class); @@ -45,6 +51,55 @@ abstract class BootstrapUtils { /* no-op */ } + /** + * Create the {@code BootstrapContext} for the specified {@linkplain Class test class}. + * + *
Uses reflection to create a {@link org.springframework.test.context.support.DefaultBootstrapContext} + * that uses a {@link org.springframework.test.context.support.DefaultCacheAwareContextLoaderDelegate}. + * + * @param testClass the test class for which the bootstrap context should be created + * @return a new {@code BootstrapContext}; never {@code null} + */ + @SuppressWarnings("unchecked") + static BootstrapContext createBootstrapContext(Class> testClass) { + CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = createCacheAwareContextLoaderDelegate(); + + Class extends BootstrapContext> clazz = null; + try { + clazz = (Class extends BootstrapContext>) ClassUtils.forName(DEFAULT_BOOTSTRAP_CONTEXT_CLASS_NAME, + BootstrapUtils.class.getClassLoader()); + + Constructor extends BootstrapContext> constructor = clazz.getConstructor(Class.class, + CacheAwareContextLoaderDelegate.class); + + if (logger.isDebugEnabled()) { + logger.debug(String.format("Instantiating BootstrapContext using constructor [%s]", constructor)); + } + return instantiateClass(constructor, testClass, cacheAwareContextLoaderDelegate); + } + catch (Throwable t) { + throw new IllegalStateException("Could not load BootstrapContext [" + clazz + "]", t); + } + } + + @SuppressWarnings("unchecked") + private static CacheAwareContextLoaderDelegate createCacheAwareContextLoaderDelegate() { + Class extends CacheAwareContextLoaderDelegate> clazz = null; + try { + clazz = (Class extends CacheAwareContextLoaderDelegate>) ClassUtils.forName( + DEFAULT_CACHE_AWARE_CONTEXT_LOADER_DELEGATE_CLASS_NAME, BootstrapUtils.class.getClassLoader()); + + if (logger.isDebugEnabled()) { + logger.debug(String.format("Instantiating CacheAwareContextLoaderDelegate from class [%s]", + clazz.getName())); + } + return instantiateClass(clazz, CacheAwareContextLoaderDelegate.class); + } + catch (Throwable t) { + throw new IllegalStateException("Could not load CacheAwareContextLoaderDelegate [" + clazz + "]", t); + } + } + /** * Resolve the {@link TestContextBootstrapper} type for the test class in the * supplied {@link BootstrapContext}, instantiate it, and provide it a reference diff --git a/spring-test/src/main/java/org/springframework/test/context/CacheAwareContextLoaderDelegate.java b/spring-test/src/main/java/org/springframework/test/context/CacheAwareContextLoaderDelegate.java index 1a59940476bf5aebc5c72389555147b5f224830a..459c5a5c9c96e9924bba60e20fd0c454dc9090b1 100644 --- a/spring-test/src/main/java/org/springframework/test/context/CacheAwareContextLoaderDelegate.java +++ b/spring-test/src/main/java/org/springframework/test/context/CacheAwareContextLoaderDelegate.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ import org.springframework.test.annotation.DirtiesContext.HierarchyMode; /** * A {@code CacheAwareContextLoaderDelegate} is responsible for {@linkplain * #loadContext loading} and {@linkplain #closeContext closing} application - * contexts, interacting transparently with a context cache behind + * contexts, interacting transparently with a {@link ContextCache} behind * the scenes. * *
Note: {@code CacheAwareContextLoaderDelegate} does not extend the @@ -38,8 +38,10 @@ public interface CacheAwareContextLoaderDelegate { * Load the {@linkplain ApplicationContext application context} for the supplied * {@link MergedContextConfiguration} by delegating to the {@link ContextLoader} * configured in the given {@code MergedContextConfiguration}. - *
If the context is present in the context cache it will simply + *
If the context is present in the {@code ContextCache} it will simply * be returned; otherwise, it will be loaded, stored in the cache, and returned. + *
The cache statistics should be logged by invoking + * {@link ContextCache#logStatistics()}. * @param mergedContextConfiguration the merged context configuration to use * to load the application context; never {@code null} * @return the application context @@ -50,7 +52,7 @@ public interface CacheAwareContextLoaderDelegate { /** * Remove the {@linkplain ApplicationContext application context} for the - * supplied {@link MergedContextConfiguration} from the context cache + * supplied {@link MergedContextConfiguration} from the {@code ContextCache} * and {@linkplain ConfigurableApplicationContext#close() close} it if it is * an instance of {@link ConfigurableApplicationContext}. *
The semantics of the supplied {@code HierarchyMode} must be honored when diff --git a/spring-test/src/main/java/org/springframework/test/context/ContextCache.java b/spring-test/src/main/java/org/springframework/test/context/ContextCache.java index 5f18399d80daa68f0dd7b5fac12dda16f2157540..cbf13e432a57c6d9db772c4eba356a78f1414f2c 100644 --- a/spring-test/src/main/java/org/springframework/test/context/ContextCache.java +++ b/spring-test/src/main/java/org/springframework/test/context/ContextCache.java @@ -16,145 +16,55 @@ package org.springframework.test.context; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; - import org.springframework.context.ApplicationContext; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.style.ToStringCreator; import org.springframework.test.annotation.DirtiesContext.HierarchyMode; -import org.springframework.util.Assert; -import org.springframework.util.ConcurrentReferenceHashMap; /** - * Cache for Spring {@link ApplicationContext ApplicationContexts} in a test - * environment. + * {@code ContextCache} defines the public API for caching Spring + * {@link ApplicationContext ApplicationContexts} within the Spring + * TestContext Framework. * - *
Caching has significant performance benefits if initializing the context - * takes a considerable about of time. Although initializing a Spring context - * itself is very quick, some beans in a context, such as a - * {@code LocalSessionFactoryBean} for working with Hibernate, may take some - * time to initialize. Hence it often makes sense to perform that initialization - * only once per test suite. + *
A {@code ContextCache} maintains a cache of {@code ApplicationContexts} + * keyed by {@link MergedContextConfiguration} instances. * - *
{@code ContextCache} maintains a cache of {@code ApplicationContexts} - * keyed by {@link MergedContextConfiguration} instances. Behind the scenes, - * Spring's {@link ConcurrentReferenceHashMap} is used to store - * {@linkplain java.lang.ref.SoftReference soft references} to cached contexts - * and {@code MergedContextConfiguration} instances. + *
Context caching can have significant performance benefits if context
+ * initialization is complex. So, although initializing a Spring context itself
+ * is typically very quick, some beans in a context — for example, an
+ * in-memory database or a {@code LocalSessionFactoryBean} for working with
+ * Hibernate — may take several seconds to initialize. Hence it often
+ * makes sense to perform that initialization only once per test suite.
*
* @author Sam Brannen
* @author Juergen Hoeller
- * @since 2.5
- * @see ConcurrentReferenceHashMap
+ * @since 4.2
*/
-class ContextCache {
-
- /**
- * Map of context keys to Spring {@code ApplicationContext} instances.
- */
- private final Map The {@link #getHitCount() hit} and {@link #getMissCount() miss} counts will
- * be updated accordingly.
+ * The {@link #getHitCount() hit} and {@link #getMissCount() miss} counts
+ * must be updated accordingly.
* @param key the context key (never {@code null})
* @return the corresponding {@code ApplicationContext} instance, or {@code null}
* if not found in the cache
* @see #remove
*/
- public ApplicationContext get(MergedContextConfiguration key) {
- Assert.notNull(key, "Key must not be null");
- ApplicationContext context = this.contextMap.get(key);
- if (context == null) {
- this.missCount.incrementAndGet();
- }
- else {
- this.hitCount.incrementAndGet();
- }
- return context;
- }
-
- /**
- * Get the overall hit count for this cache.
- * A hit is any access to the cache that returns a non-null
- * context for the queried key.
- */
- public int getHitCount() {
- return this.hitCount.get();
- }
-
- /**
- * Get the overall miss count for this cache.
- * A miss is any access to the cache that returns a {@code null}
- * context for the queried key.
- */
- public int getMissCount() {
- return this.missCount.get();
- }
+ ApplicationContext get(MergedContextConfiguration key);
/**
* Explicitly add an {@code ApplicationContext} instance to the cache
@@ -162,122 +72,79 @@ class ContextCache {
* @param key the context key (never {@code null})
* @param context the {@code ApplicationContext} instance (never {@code null})
*/
- public void put(MergedContextConfiguration key, ApplicationContext context) {
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(context, "ApplicationContext must not be null");
-
- this.contextMap.put(key, context);
- MergedContextConfiguration child = key;
- MergedContextConfiguration parent = child.getParent();
- while (parent != null) {
- Set Generally speaking, you would only call this method if you change the
- * state of a singleton bean, potentially affecting future interaction with
- * the context.
- * In addition, the semantics of the supplied {@code HierarchyMode} will
+ * {@linkplain org.springframework.context.ConfigurableApplicationContext#close() close}
+ * it if it is an instance of {@code ConfigurableApplicationContext}.
+ * Generally speaking, this method should be called if the state of
+ * a singleton bean has been modified, potentially affecting future
+ * interaction with the context.
+ * In addition, the semantics of the supplied {@code HierarchyMode} must
* be honored. See the Javadoc for {@link HierarchyMode} for details.
* @param key the context key; never {@code null}
* @param hierarchyMode the hierarchy mode; may be {@code null} if the context
* is not part of a hierarchy
*/
- public void remove(MergedContextConfiguration key, HierarchyMode hierarchyMode) {
- Assert.notNull(key, "Key must not be null");
-
- // startKey is the level at which to begin clearing the cache, depending
- // on the configured hierarchy mode.
- MergedContextConfiguration startKey = key;
- if (hierarchyMode == HierarchyMode.EXHAUSTIVE) {
- while (startKey.getParent() != null) {
- startKey = startKey.getParent();
- }
- }
-
- List If the cache contains more than {@code Integer.MAX_VALUE} elements,
+ * this method must return {@code Integer.MAX_VALUE}.
+ */
+ int size();
- // Remove empty entries from the hierarchy map.
- for (MergedContextConfiguration currentKey : this.hierarchyMap.keySet()) {
- if (this.hierarchyMap.get(currentKey).isEmpty()) {
- this.hierarchyMap.remove(currentKey);
- }
- }
- }
+ /**
+ * Determine the number of parent contexts currently tracked within the cache.
+ */
+ int getParentContextCount();
- private void remove(List A hit is any access to the cache that returns a non-null
+ * context for the queried key.
+ */
+ int getHitCount();
- Set A miss is any access to the cache that returns a {@code null}
+ * context for the queried key.
+ */
+ int getMissCount();
- // Physically remove and close leaf nodes first (i.e., on the way back up the
- // stack as opposed to prior to the recursive call).
- ApplicationContext context = this.contextMap.remove(key);
- if (context instanceof ConfigurableApplicationContext) {
- ((ConfigurableApplicationContext) context).close();
- }
- removedContexts.add(key);
- }
+ /**
+ * Reset all state maintained by this cache including statistics.
+ * @see #clear()
+ * @see #clearStatistics()
+ */
+ void reset();
/**
- * Determine the number of contexts currently stored in the cache.
- * If the cache contains more than {@code Integer.MAX_VALUE} elements,
- * this method returns {@code Integer.MAX_VALUE}.
+ * Clear all contexts from the cache, clearing context hierarchy information as well.
*/
- public int size() {
- return this.contextMap.size();
- }
+ void clear();
/**
- * Determine the number of parent contexts currently tracked within the cache.
+ * Clear hit and miss count statistics for the cache (i.e., reset counters to zero).
*/
- public int getParentContextCount() {
- return this.hierarchyMap.size();
- }
+ void clearStatistics();
/**
- * Generate a text string containing the statistics for this cache.
- * Specifically, the returned string contains the {@linkplain #size},
- * {@linkplain #getHitCount() hit count}, {@linkplain #getMissCount() miss count},
- * and {@linkplain #getParentContextCount() parent context count}.
- * @return the statistics for this cache
+ * Log the statistics for this {@code ContextCache} at {@code DEBUG} level
+ * using the {@value #CONTEXT_CACHE_LOGGING_CATEGORY} logging category.
+ * The following information should be logged.
+ * A custom bootstrapping strategy can be configured for a test class via
- * {@link BootstrapWith @BootstrapWith}, either directly or as a meta-annotation.
- * See {@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration}
- * for an example.
- *
- * The {@link TestContextManager} uses a {@code TestContextBootstrapper} to
+ * A {@code TestContextBootstrapper} is used by the {@link TestContextManager} to
* {@linkplain #getTestExecutionListeners get the TestExecutionListeners} for the
- * current test and to {@linkplain #buildMergedContextConfiguration build the
- * merged context configuration} necessary to create the {@link TestContext} that
+ * current test and to {@linkplain #buildTestContext build the TestContext} that
* it manages.
*
+ * A custom bootstrapping strategy can be configured for a test class (or
+ * test class hierarchy) via {@link BootstrapWith @BootstrapWith}, either
+ * directly or as a meta-annotation. See
+ * {@link org.springframework.test.context.web.WebAppConfiguration @WebAppConfiguration}
+ * for an example.
+ *
+ * If a bootstrapper is not explicitly configured via {@code @BootstrapWith}, the
+ * {@link org.springframework.test.context.support.DefaultTestContextBootstrapper DefaultTestContextBootstrapper}
+ * will be used.
+ *
+ * Concrete implementations must provide a {@code public} no-args constructor.
*
- * Note: this SPI might potentially change in the future in
+ * WARNING: this SPI will likely change in the future in
* order to accommodate new requirements. Implementers are therefore strongly encouraged
- * not to implement this interface directly but rather to extend
+ * not to implement this interface directly but rather to extend
* {@link org.springframework.test.context.support.AbstractTestContextBootstrapper
* AbstractTestContextBootstrapper} or one of its concrete subclasses instead.
*
@@ -59,28 +67,13 @@ public interface TestContextBootstrapper {
BootstrapContext getBootstrapContext();
/**
- * Get a list of newly instantiated {@link TestExecutionListener TestExecutionListeners}
- * for the test class in the {@link BootstrapContext} associated with this bootstrapper.
- * If {@link TestExecutionListeners @TestExecutionListeners} is not
- * present on the test class in the {@code BootstrapContext},
- * default listeners should be returned. Furthermore, default
- * listeners must be sorted using
- * {@link org.springframework.core.annotation.AnnotationAwareOrderComparator
- * AnnotationAwareOrderComparator}.
- * Concrete implementations are free to determine what comprises the
- * set of default listeners. However, by default, the Spring TestContext
- * Framework will use the
- * {@link org.springframework.core.io.support.SpringFactoriesLoader SpringFactoriesLoader}
- * mechanism to look up all {@code TestExecutionListener} class names
- * configured in all {@code META-INF/spring.factories} files on the classpath.
- * The {@link TestExecutionListeners#inheritListeners() inheritListeners}
- * flag of {@link TestExecutionListeners @TestExecutionListeners} must be
- * taken into consideration. Specifically, if the {@code inheritListeners}
- * flag is set to {@code true}, listeners declared for a given test class must
- * be appended to the end of the list of listeners declared in superclasses.
- * @return a list of {@code TestExecutionListener} instances
+ * Build the {@link TestContext} for the {@link BootstrapContext}
+ * associated with this bootstrapper.
+ * @return a new {@link TestContext}, never {@code null}
+ * @since 4.2
+ * @see #buildMergedContextConfiguration()
*/
- List Consult the Javadoc for the aforementioned annotations for details on
* the required semantics.
+ * Note that the implementation of {@link #buildTestContext()} should
+ * typically delegate to this method when constructing the {@code TestContext}.
* When determining which {@link ContextLoader} to use for a given test
* class, the following algorithm should be used:
* If {@link TestExecutionListeners @TestExecutionListeners} is not
+ * present on the test class in the {@code BootstrapContext},
+ * default listeners should be returned. Furthermore, default
+ * listeners must be sorted using
+ * {@link org.springframework.core.annotation.AnnotationAwareOrderComparator
+ * AnnotationAwareOrderComparator}.
+ * Concrete implementations are free to determine what comprises the
+ * set of default listeners. However, by default, the Spring TestContext
+ * Framework will use the
+ * {@link org.springframework.core.io.support.SpringFactoriesLoader SpringFactoriesLoader}
+ * mechanism to look up all {@code TestExecutionListener} class names
+ * configured in all {@code META-INF/spring.factories} files on the classpath.
+ * The {@link TestExecutionListeners#inheritListeners() inheritListeners}
+ * flag of {@link TestExecutionListeners @TestExecutionListeners} must be
+ * taken into consideration. Specifically, if the {@code inheritListeners}
+ * flag is set to {@code true}, listeners declared for a given test class must
+ * be appended to the end of the list of listeners declared in superclasses.
+ * @return a list of {@code TestExecutionListener} instances
+ */
+ List Specifically, a {@code TestContextManager} is responsible for managing a
* single {@link TestContext} and signaling events to all registered
- * {@link TestExecutionListener TestExecutionListeners} at well defined test
+ * {@link TestExecutionListener TestExecutionListeners} at the following test
* execution points:
*
* Support for loading and accessing
+ * {@link org.springframework.context.ApplicationContext application contexts},
+ * dependency injection of test instances,
+ * {@link org.springframework.transaction.annotation.Transactional transactional}
+ * execution of test methods, etc. is provided by
+ * {@link SmartContextLoader ContextLoaders} and {@link TestExecutionListener
+ * TestExecutionListeners}, which are configured via
+ * {@link ContextConfiguration @ContextConfiguration} and
+ * {@link TestExecutionListeners @TestExecutionListeners}.
+ *
+ * Bootstrapping of the {@code TestContext}, the default {@code ContextLoader},
+ * default {@code TestExecutionListeners}, and their collaborators is performed
+ * by a {@link TestContextBootstrapper}, which is configured via
+ * {@link BootstrapWith @BootstrapWith}.
+ *
* @author Sam Brannen
* @author Juergen Hoeller
* @since 2.5
@@ -73,37 +84,42 @@ public class TestContextManager {
private static final Log logger = LogFactory.getLog(TestContextManager.class);
- /**
- * Cache of Spring application contexts.
- * This needs to be static, since test instances may be destroyed and
- * recreated between invocations of individual test methods, as is the case
- * with JUnit.
- */
- static final ContextCache contextCache = new ContextCache();
-
private final TestContext testContext;
- private final TestContextBootstrapper testContextBootstrapper;
-
private final List Delegates to a {@link TestContextBootstrapper} for building the {@code TestContext}
+ * and retrieving the {@code TestExecutionListeners}.
* @param testClass the test class to be managed
+ * @see TestContextBootstrapper#buildTestContext
+ * @see TestContextBootstrapper#getTestExecutionListeners
* @see #registerTestExecutionListeners
*/
public TestContextManager(Class> testClass) {
- CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = new DefaultCacheAwareContextLoaderDelegate(contextCache);
- BootstrapContext bootstrapContext = new DefaultBootstrapContext(testClass, cacheAwareContextLoaderDelegate);
- this.testContextBootstrapper = BootstrapUtils.resolveTestContextBootstrapper(bootstrapContext);
- this.testContext = new DefaultTestContext(this.testContextBootstrapper);
- registerTestExecutionListeners(this.testContextBootstrapper.getTestExecutionListeners());
+ BootstrapContext bootstrapContext = createBootstrapContext(testClass);
+ TestContextBootstrapper bootstrapper = BootstrapUtils.resolveTestContextBootstrapper(bootstrapContext);
+ this.testContext = bootstrapper.buildTestContext();
+ registerTestExecutionListeners(bootstrapper.getTestExecutionListeners());
}
+ /**
+ * Create the {@code BootstrapContext} for the specified {@linkplain Class test class}.
+ * The default implementation creates a
+ * {@link org.springframework.test.context.support.DefaultBootstrapContext DefaultBootstrapContext}
+ * that uses a
+ * {@link org.springframework.test.context.support.DefaultCacheAwareContextLoaderDelegate DefaultCacheAwareContextLoaderDelegate}.
+ * Can be overridden by subclasses as necessary.
+ * @param testClass the test class for which the bootstrap context should be created
+ * @return a new {@code BootstrapContext}; never {@code null}
+ */
+ protected BootstrapContext createBootstrapContext(Class> testClass) {
+ return BootstrapUtils.createBootstrapContext(testClass);
+ }
/**
* Get the {@link TestContext} managed by this {@code TestContextManager}.
@@ -201,7 +217,7 @@ public class TestContextManager {
* @see #getTestExecutionListeners()
*/
public void prepareTestInstance(Object testInstance) throws Exception {
- Assert.notNull(testInstance, "testInstance must not be null");
+ Assert.notNull(testInstance, "Test instance must not be null");
if (logger.isTraceEnabled()) {
logger.trace("prepareTestInstance(): instance [" + testInstance + "]");
}
@@ -282,7 +298,7 @@ public class TestContextManager {
* @see #getTestExecutionListeners()
*/
public void afterTestMethod(Object testInstance, Method testMethod, Throwable exception) throws Exception {
- Assert.notNull(testInstance, "testInstance must not be null");
+ Assert.notNull(testInstance, "Test instance must not be null");
if (logger.isTraceEnabled()) {
logger.trace("afterTestMethod(): instance [" + testInstance + "], method [" + testMethod +
"], exception [" + exception + "]");
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java b/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java
index 709c82e972ab52e09b695b79fba3ab2d383e5279..c6b1b0b15c53efb953b055498ebe33a5531e65ca 100644
--- a/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/AbstractTestContextBootstrapper.java
@@ -38,12 +38,14 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.test.context.BootstrapContext;
import org.springframework.test.context.CacheAwareContextLoaderDelegate;
+import org.springframework.test.context.ContextCache;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.ContextLoader;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.SmartContextLoader;
+import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestContextBootstrapper;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.TestExecutionListeners;
@@ -66,6 +68,9 @@ import org.springframework.util.StringUtils;
* To plug in custom {@link ContextCache} support, override
+ * {@link #getCacheAwareContextLoaderDelegate()}.
+ *
* @author Sam Brannen
* @author Juergen Hoeller
* @since 4.1
@@ -93,6 +98,21 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
return this.bootstrapContext;
}
+ /**
+ * Build a new {@link DefaultTestContext} using the {@linkplain Class test class}
+ * in the {@link BootstrapContext} associated with this bootstrapper and
+ * by delegating to {@link #buildMergedContextConfiguration()} and
+ * {@link #getCacheAwareContextLoaderDelegate()}.
+ * Concrete subclasses may choose to override this method to return a
+ * custom {@link TestContext} implementation.
+ * @since 4.2
+ */
+ @Override
+ public TestContext buildTestContext() {
+ return new DefaultTestContext(getBootstrapContext().getTestClass(), buildMergedContextConfiguration(),
+ getCacheAwareContextLoaderDelegate());
+ }
+
/**
* {@inheritDoc}
*/
@@ -266,7 +286,7 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
@Override
public final MergedContextConfiguration buildMergedContextConfiguration() {
Class> testClass = getBootstrapContext().getTestClass();
- CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = getBootstrapContext().getCacheAwareContextLoaderDelegate();
+ CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = getCacheAwareContextLoaderDelegate();
if (MetaAnnotationUtils.findAnnotationDescriptorForTypes(testClass, ContextConfiguration.class,
ContextHierarchy.class) == null) {
@@ -455,6 +475,20 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot
return null;
}
+ /**
+ * Get the {@link CacheAwareContextLoaderDelegate} to use for transparent
+ * interaction with the {@code ContextCache}.
+ * The default implementation simply delegates to
+ * {@code getBootstrapContext().getCacheAwareContextLoaderDelegate()}.
+ * Concrete subclasses may choose to override this method to return a
+ * custom {@code CacheAwareContextLoaderDelegate} implementation with
+ * custom {@link ContextCache} support.
+ * @return the context loader delegate (never {@code null})
+ */
+ protected CacheAwareContextLoaderDelegate getCacheAwareContextLoaderDelegate() {
+ return getBootstrapContext().getCacheAwareContextLoaderDelegate();
+ }
+
/**
* Determine the default {@link ContextLoader} {@linkplain Class class}
* to use for the supplied test class.
diff --git a/spring-test/src/main/java/org/springframework/test/context/DefaultBootstrapContext.java b/spring-test/src/main/java/org/springframework/test/context/support/DefaultBootstrapContext.java
similarity index 70%
rename from spring-test/src/main/java/org/springframework/test/context/DefaultBootstrapContext.java
rename to spring-test/src/main/java/org/springframework/test/context/support/DefaultBootstrapContext.java
index e40802a7be10844e29c2f865b4a503bd1932dd76..d541c29b0d3f28478b58bcdd7ed62fbedfe0454f 100644
--- a/spring-test/src/main/java/org/springframework/test/context/DefaultBootstrapContext.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/DefaultBootstrapContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-package org.springframework.test.context;
+package org.springframework.test.context.support;
import org.springframework.core.style.ToStringCreator;
+import org.springframework.test.context.BootstrapContext;
+import org.springframework.test.context.CacheAwareContextLoaderDelegate;
import org.springframework.util.Assert;
/**
@@ -25,13 +27,19 @@ import org.springframework.util.Assert;
* @author Sam Brannen
* @since 4.1
*/
-class DefaultBootstrapContext implements BootstrapContext {
+public class DefaultBootstrapContext implements BootstrapContext {
private final Class> testClass;
private final CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate;
- DefaultBootstrapContext(Class> testClass, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
+ /**
+ * Construct a new {@code DefaultBootstrapContext} from the supplied arguments.
+ * @param testClass the test class for this bootstrap context; never {@code null}
+ * @param cacheAwareContextLoaderDelegate the context loader delegate to use for
+ * transparent interaction with the {@code ContextCache}; never {@code null}
+ */
+ public DefaultBootstrapContext(Class> testClass, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
Assert.notNull(testClass, "Test class must not be null");
Assert.notNull(cacheAwareContextLoaderDelegate, "CacheAwareContextLoaderDelegate must not be null");
this.testClass = testClass;
diff --git a/spring-test/src/main/java/org/springframework/test/context/DefaultCacheAwareContextLoaderDelegate.java b/spring-test/src/main/java/org/springframework/test/context/support/DefaultCacheAwareContextLoaderDelegate.java
similarity index 64%
rename from spring-test/src/main/java/org/springframework/test/context/DefaultCacheAwareContextLoaderDelegate.java
rename to spring-test/src/main/java/org/springframework/test/context/support/DefaultCacheAwareContextLoaderDelegate.java
index a9d38dfa08db849a70b1d22f795f1688b8476748..7598fa9eb91f75728fd02901ec683b2cec60fafb 100644
--- a/spring-test/src/main/java/org/springframework/test/context/DefaultCacheAwareContextLoaderDelegate.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/DefaultCacheAwareContextLoaderDelegate.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,47 +14,78 @@
* limitations under the License.
*/
-package org.springframework.test.context;
+package org.springframework.test.context.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
+import org.springframework.test.context.CacheAwareContextLoaderDelegate;
+import org.springframework.test.context.ContextCache;
+import org.springframework.test.context.ContextLoader;
+import org.springframework.test.context.MergedContextConfiguration;
+import org.springframework.test.context.SmartContextLoader;
import org.springframework.util.Assert;
/**
* Default implementation of the {@link CacheAwareContextLoaderDelegate} interface.
*
- * Although {@code DefaultCacheAwareContextLoaderDelegate} was first introduced
- * in Spring Framework 4.1, the initial implementation of this class was extracted
- * from the existing code base for {@code CacheAwareContextLoaderDelegate} when
- * {@code CacheAwareContextLoaderDelegate} was converted into an interface.
+ * To use a static {@code DefaultContextCache}, invoke the
+ * {@link #DefaultCacheAwareContextLoaderDelegate()} constructor; otherwise,
+ * invoke the {@link #DefaultCacheAwareContextLoaderDelegate(ContextCache)}
+ * and provide a custom {@link ContextCache} implementation.
*
* @author Sam Brannen
* @since 4.1
*/
-class DefaultCacheAwareContextLoaderDelegate implements CacheAwareContextLoaderDelegate {
+public class DefaultCacheAwareContextLoaderDelegate implements CacheAwareContextLoaderDelegate {
private static final Log logger = LogFactory.getLog(DefaultCacheAwareContextLoaderDelegate.class);
- private static final Log statsLogger = LogFactory.getLog("org.springframework.test.context.cache");
+ /**
+ * Default static cache of Spring application contexts.
+ */
+ static final ContextCache defaultContextCache = new DefaultContextCache();
private final ContextCache contextCache;
- DefaultCacheAwareContextLoaderDelegate(ContextCache contextCache) {
+ /**
+ * Construct a new {@code DefaultCacheAwareContextLoaderDelegate} using
+ * a static {@link DefaultContextCache}.
+ * This default cache is static so that each context can be cached
+ * and reused for all subsequent tests that declare the same unique
+ * context configuration within the same JVM process.
+ * @see #DefaultCacheAwareContextLoaderDelegate(ContextCache)
+ */
+ public DefaultCacheAwareContextLoaderDelegate() {
+ this(defaultContextCache);
+ }
+
+ /**
+ * Construct a new {@code DefaultCacheAwareContextLoaderDelegate} using
+ * the supplied {@link ContextCache}.
+ * @see #DefaultCacheAwareContextLoaderDelegate()
+ */
+ public DefaultCacheAwareContextLoaderDelegate(ContextCache contextCache) {
Assert.notNull(contextCache, "ContextCache must not be null");
this.contextCache = contextCache;
}
+ /**
+ * Get the {@link ContextCache} used by this context loader delegate.
+ */
+ protected ContextCache getContextCache() {
+ return this.contextCache;
+ }
/**
* Load the {@code ApplicationContext} for the supplied merged context configuration.
* Supports both the {@link SmartContextLoader} and {@link ContextLoader} SPIs.
* @throws Exception if an error occurs while loading the application context
*/
- private ApplicationContext loadContextInternal(MergedContextConfiguration mergedContextConfiguration)
+ protected ApplicationContext loadContextInternal(MergedContextConfiguration mergedContextConfiguration)
throws Exception {
ContextLoader contextLoader = mergedContextConfiguration.getContextLoader();
@@ -101,9 +132,7 @@ class DefaultCacheAwareContextLoaderDelegate implements CacheAwareContextLoaderD
}
}
- if (statsLogger.isDebugEnabled()) {
- statsLogger.debug("Spring test ApplicationContext cache statistics: " + this.contextCache);
- }
+ this.contextCache.logStatistics();
return context;
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/support/DefaultContextCache.java b/spring-test/src/main/java/org/springframework/test/context/support/DefaultContextCache.java
new file mode 100644
index 0000000000000000000000000000000000000000..bcb5c21df45d0bd1a1409491c311fecb896c84a1
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/context/support/DefaultContextCache.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2002-2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.test.context.support;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.style.ToStringCreator;
+import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
+import org.springframework.test.context.ContextCache;
+import org.springframework.test.context.MergedContextConfiguration;
+import org.springframework.util.Assert;
+import org.springframework.util.ConcurrentReferenceHashMap;
+
+/**
+ * Default implementation of the {@link ContextCache} API.
+ *
+ * Uses Spring's {@link ConcurrentReferenceHashMap} to store
+ * {@linkplain java.lang.ref.SoftReference soft references} to cached
+ * contexts and {@code MergedContextConfiguration} instances.
+ *
+ * @author Sam Brannen
+ * @author Juergen Hoeller
+ * @since 2.5
+ * @see ConcurrentReferenceHashMap
+ */
+public class DefaultContextCache implements ContextCache {
+
+ private static final Log statsLogger = LogFactory.getLog(CONTEXT_CACHE_LOGGING_CATEGORY);
+
+ /**
+ * Map of context keys to Spring {@code ApplicationContext} instances.
+ */
+ private final Map The string returned by this method contains all information
+ * required for compliance with the contract for {@link #logStatistics()}.
+ * @return a string representation of this cache, including statistics
+ */
+ @Override
+ public String toString() {
+ return new ToStringCreator(this)
+ .append("size", size())
+ .append("parentContextCount", getParentContextCount())
+ .append("hitCount", getHitCount())
+ .append("missCount", getMissCount())
+ .toString();
+ }
+
+}
diff --git a/spring-test/src/main/java/org/springframework/test/context/DefaultTestContext.java b/spring-test/src/main/java/org/springframework/test/context/support/DefaultTestContext.java
similarity index 55%
rename from spring-test/src/main/java/org/springframework/test/context/DefaultTestContext.java
rename to spring-test/src/main/java/org/springframework/test/context/support/DefaultTestContext.java
index eac358e8e52d531e71527d2e4535d2411065176a..bd730b32546568345f5e47558b8c6f7db55a3cee 100644
--- a/spring-test/src/main/java/org/springframework/test/context/DefaultTestContext.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/DefaultTestContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.test.context;
+package org.springframework.test.context.support;
import java.lang.reflect.Method;
@@ -22,21 +22,19 @@ import org.springframework.context.ApplicationContext;
import org.springframework.core.AttributeAccessorSupport;
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.TestContext;
import org.springframework.util.Assert;
/**
* Default implementation of the {@link TestContext} interface.
*
- * Although {@code DefaultTestContext} was first introduced in Spring Framework
- * 4.0, the initial implementation of this class was extracted from the existing
- * code base for {@code TestContext} when {@code TestContext} was converted into
- * an interface.
- *
* @author Sam Brannen
* @author Juergen Hoeller
* @since 4.0
*/
-class DefaultTestContext extends AttributeAccessorSupport implements TestContext {
+public class DefaultTestContext extends AttributeAccessorSupport implements TestContext {
private static final long serialVersionUID = -5827157174866681233L;
@@ -54,24 +52,42 @@ class DefaultTestContext extends AttributeAccessorSupport implements TestContext
/**
- * Construct a new test context using the supplied {@link TestContextBootstrapper}.
- * @param testContextBootstrapper the {@code TestContextBootstrapper} to use
- * to construct the test context (must not be {@code null})
+ * Construct a new {@code DefaultTestContext} from the supplied arguments.
+ * @param testClass the test class for this test context; never {@code null}
+ * @param mergedContextConfiguration the merged application context
+ * configuration for this test context; never {@code null}
+ * @param cacheAwareContextLoaderDelegate the delegate to use for loading
+ * and closing the application context for this test context; never {@code null}
*/
- DefaultTestContext(TestContextBootstrapper testContextBootstrapper) {
- Assert.notNull(testContextBootstrapper, "TestContextBootstrapper must not be null");
-
- BootstrapContext bootstrapContext = testContextBootstrapper.getBootstrapContext();
- this.testClass = bootstrapContext.getTestClass();
- this.cacheAwareContextLoaderDelegate = bootstrapContext.getCacheAwareContextLoaderDelegate();
- this.mergedContextConfiguration = testContextBootstrapper.buildMergedContextConfiguration();
+ public DefaultTestContext(Class> testClass, MergedContextConfiguration mergedContextConfiguration,
+ CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
+ Assert.notNull(testClass, "testClass must not be null");
+ Assert.notNull(mergedContextConfiguration, "MergedContextConfiguration must not be null");
+ Assert.notNull(cacheAwareContextLoaderDelegate, "CacheAwareContextLoaderDelegate must not be null");
+ this.testClass = testClass;
+ this.mergedContextConfiguration = mergedContextConfiguration;
+ this.cacheAwareContextLoaderDelegate = cacheAwareContextLoaderDelegate;
}
-
+ /**
+ * Get the {@linkplain ApplicationContext application context} for this
+ * test context.
+ * The default implementation delegates to the {@link CacheAwareContextLoaderDelegate}
+ * that was supplied when this {@code TestContext} was constructed.
+ * @see CacheAwareContextLoaderDelegate#loadContext
+ */
public ApplicationContext getApplicationContext() {
return this.cacheAwareContextLoaderDelegate.loadContext(this.mergedContextConfiguration);
}
+ /**
+ * Mark the {@linkplain ApplicationContext application context} associated
+ * with this test context as dirty (i.e., by removing it from the
+ * context cache and closing it).
+ * The default implementation delegates to the {@link CacheAwareContextLoaderDelegate}
+ * that was supplied when this {@code TestContext} was constructed.
+ * @see CacheAwareContextLoaderDelegate#closeContext
+ */
public void markApplicationContextDirty(HierarchyMode hierarchyMode) {
this.cacheAwareContextLoaderDelegate.closeContext(this.mergedContextConfiguration, hierarchyMode);
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/BootstrapTestUtils.java b/spring-test/src/test/java/org/springframework/test/context/BootstrapTestUtils.java
index b3b4fa5fb40402992794c49722ebff5fb189146b..af6c4f7f5eaaba2de3ab149228ac2f70f50a4ccc 100644
--- a/spring-test/src/test/java/org/springframework/test/context/BootstrapTestUtils.java
+++ b/spring-test/src/test/java/org/springframework/test/context/BootstrapTestUtils.java
@@ -16,6 +16,8 @@
package org.springframework.test.context;
+import org.springframework.test.context.support.DefaultBootstrapContext;
+
/**
* Collection of test-related utility methods for working with {@link BootstrapContext
* BootstrapContexts} and {@link TestContextBootstrapper TestContextBootstrappers}.
diff --git a/spring-test/src/test/java/org/springframework/test/context/ClassLevelDirtiesContextTestNGTests.java b/spring-test/src/test/java/org/springframework/test/context/ClassLevelDirtiesContextTestNGTests.java
index 9009e73229a5a8eb09c938a05d2787b9b7dc3dc5..3b45f158d6b8fa974e229aac9a2d3d16312feaf7 100644
--- a/spring-test/src/test/java/org/springframework/test/context/ClassLevelDirtiesContextTestNGTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/ClassLevelDirtiesContextTestNGTests.java
@@ -37,7 +37,7 @@ import org.springframework.test.context.testng.TrackingTestNGTestListener;
import org.testng.TestNG;
import static org.junit.Assert.*;
-import static org.springframework.test.context.ContextCacheTestUtils.*;
+import static org.springframework.test.context.support.ContextCacheTestUtils.*;
/**
* JUnit 4 based integration test which verifies correct {@linkplain ContextCache
@@ -79,8 +79,9 @@ public class ClassLevelDirtiesContextTestNGTests {
@BeforeClass
public static void verifyInitialCacheState() {
- ContextCache contextCache = TestContextManager.contextCache;
- contextCache.reset();
+ resetContextCache();
+ // Reset static counters in case tests are run multiple times in a test suite --
+ // for example, via JUnit's @Suite.
cacheHits.set(0);
cacheMisses.set(0);
assertContextCacheStatistics("BeforeClass", 0, cacheHits.get(), cacheMisses.get());
diff --git a/spring-test/src/test/java/org/springframework/test/context/ClassLevelDirtiesContextTests.java b/spring-test/src/test/java/org/springframework/test/context/ClassLevelDirtiesContextTests.java
index 1c51ad8072e0486a693fcfc54bf8521ac5418f75..64dced85b8573bfc323a5dcda941af0169597070 100644
--- a/spring-test/src/test/java/org/springframework/test/context/ClassLevelDirtiesContextTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/ClassLevelDirtiesContextTests.java
@@ -36,7 +36,7 @@ import org.springframework.test.context.support.DependencyInjectionTestExecution
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
import static org.junit.Assert.*;
-import static org.springframework.test.context.ContextCacheTestUtils.*;
+import static org.springframework.test.context.support.ContextCacheTestUtils.*;
/**
* JUnit 4 based integration test which verifies correct {@linkplain ContextCache
@@ -74,8 +74,9 @@ public class ClassLevelDirtiesContextTests {
@BeforeClass
public static void verifyInitialCacheState() {
- ContextCache contextCache = TestContextManager.contextCache;
- contextCache.reset();
+ resetContextCache();
+ // Reset static counters in case tests are run multiple times in a test suite --
+ // for example, via JUnit's @Suite.
cacheHits.set(0);
cacheMisses.set(0);
assertContextCacheStatistics("BeforeClass", 0, cacheHits.get(), cacheMisses.get());
diff --git a/spring-test/src/test/java/org/springframework/test/context/ContextCacheTests.java b/spring-test/src/test/java/org/springframework/test/context/ContextCacheTests.java
index 61dc9beaa3212eab80ea45456fd75d1b1fbe7f53..e8a4c3e48fdea54c7888dd7cdbc439c1cdcf3867 100644
--- a/spring-test/src/test/java/org/springframework/test/context/ContextCacheTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/ContextCacheTests.java
@@ -23,10 +23,11 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext.HierarchyMode;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
+import org.springframework.test.context.support.DefaultContextCache;
import org.springframework.test.util.ReflectionTestUtils;
import static org.junit.Assert.*;
-import static org.springframework.test.context.ContextCacheTestUtils.*;
+import static org.springframework.test.context.support.ContextCacheTestUtils.*;
/**
* Integration tests for verifying proper behavior of the {@link ContextCache} in
@@ -39,7 +40,7 @@ import static org.springframework.test.context.ContextCacheTestUtils.*;
*/
public class ContextCacheTests {
- private ContextCache contextCache = new ContextCache();
+ private ContextCache contextCache = new DefaultContextCache();
@Before
diff --git a/spring-test/src/test/java/org/springframework/test/context/SpringRunnerContextCacheTests.java b/spring-test/src/test/java/org/springframework/test/context/SpringRunnerContextCacheTests.java
index 3e09f2731e90f7b43e1509a8c13ed1702065b899..622d25f88dfd0b8efff5ece1dbf3b96991ac0d10 100644
--- a/spring-test/src/test/java/org/springframework/test/context/SpringRunnerContextCacheTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/SpringRunnerContextCacheTests.java
@@ -31,7 +31,7 @@ import org.springframework.test.context.support.DependencyInjectionTestExecution
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
import static org.junit.Assert.*;
-import static org.springframework.test.context.ContextCacheTestUtils.*;
+import static org.springframework.test.context.support.ContextCacheTestUtils.*;
/**
* JUnit 4 based unit test which verifies correct {@link ContextCache
@@ -58,8 +58,7 @@ public class SpringRunnerContextCacheTests {
@BeforeClass
public static void verifyInitialCacheState() {
dirtiedApplicationContext = null;
- ContextCache contextCache = TestContextManager.contextCache;
- contextCache.reset();
+ resetContextCache();
assertContextCacheStatistics("BeforeClass", 0, 0, 0);
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/TestContextTestUtils.java b/spring-test/src/test/java/org/springframework/test/context/TestContextTestUtils.java
index 3f1eb86edfbf2ba7099eba08f2aee0e25c0209ec..47ec34ebd589057c9e2c0dbb085b6223ffe7f5c0 100644
--- a/spring-test/src/test/java/org/springframework/test/context/TestContextTestUtils.java
+++ b/spring-test/src/test/java/org/springframework/test/context/TestContextTestUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,9 @@
package org.springframework.test.context;
+import org.springframework.test.context.support.DefaultBootstrapContext;
+import org.springframework.test.context.support.DefaultCacheAwareContextLoaderDelegate;
+
/**
* Collection of test-related utility methods for working with {@link TestContext TestContexts}.
*
@@ -28,12 +31,12 @@ public abstract class TestContextTestUtils {
return buildTestContext(testClass, new DefaultCacheAwareContextLoaderDelegate(contextCache));
}
- public static TestContext buildTestContext(
- Class> testClass, CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
+ public static TestContext buildTestContext(Class> testClass,
+ CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate) {
BootstrapContext bootstrapContext = new DefaultBootstrapContext(testClass, cacheAwareContextLoaderDelegate);
TestContextBootstrapper testContextBootstrapper = BootstrapUtils.resolveTestContextBootstrapper(bootstrapContext);
- return new DefaultTestContext(testContextBootstrapper);
+ return testContextBootstrapper.buildTestContext();
}
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/ContextCacheTestUtils.java b/spring-test/src/test/java/org/springframework/test/context/support/ContextCacheTestUtils.java
similarity index 78%
rename from spring-test/src/test/java/org/springframework/test/context/ContextCacheTestUtils.java
rename to spring-test/src/test/java/org/springframework/test/context/support/ContextCacheTestUtils.java
index dd5efcee1a43a7b6391ba0dbc6f0ccdc93a562f1..c6127b47c74b90cfd04047edc58250576043548c 100644
--- a/spring-test/src/test/java/org/springframework/test/context/ContextCacheTestUtils.java
+++ b/spring-test/src/test/java/org/springframework/test/context/support/ContextCacheTestUtils.java
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-package org.springframework.test.context;
+package org.springframework.test.context.support;
+
+import org.springframework.test.context.ContextCache;
import static org.junit.Assert.*;
@@ -28,7 +30,14 @@ import static org.junit.Assert.*;
public class ContextCacheTestUtils {
/**
- * Assert the statistics of the context cache in {@link TestContextManager}.
+ * Reset the state of the static context cache in {@link DefaultCacheAwareContextLoaderDelegate}.
+ */
+ public static final void resetContextCache() {
+ DefaultCacheAwareContextLoaderDelegate.defaultContextCache.reset();
+ }
+
+ /**
+ * Assert the statistics of the static context cache in {@link DefaultCacheAwareContextLoaderDelegate}.
*
* @param usageScenario the scenario in which the statistics are used
* @param expectedSize the expected number of contexts in the cache
@@ -37,8 +46,8 @@ public class ContextCacheTestUtils {
*/
public static final void assertContextCacheStatistics(String usageScenario, int expectedSize, int expectedHitCount,
int expectedMissCount) {
- assertContextCacheStatistics(TestContextManager.contextCache, usageScenario, expectedSize, expectedHitCount,
- expectedMissCount);
+ assertContextCacheStatistics(DefaultCacheAwareContextLoaderDelegate.defaultContextCache, usageScenario,
+ expectedSize, expectedHitCount, expectedMissCount);
}
/**
+ *
*/
- @Override
- public String toString() {
- return new ToStringCreator(this)
- .append("size", size())
- .append("hitCount", getHitCount())
- .append("missCount", getMissCount())
- .append("parentContextCount", getParentContextCount())
- .toString();
- }
+ void logStatistics();
}
diff --git a/spring-test/src/main/java/org/springframework/test/context/TestContextBootstrapper.java b/spring-test/src/main/java/org/springframework/test/context/TestContextBootstrapper.java
index 7252657f42b79fe7dbbfa6548d11f9a509ce4ff0..d33c3db8da4c29f27071b9c4ab5a76c6f4211853 100644
--- a/spring-test/src/main/java/org/springframework/test/context/TestContextBootstrapper.java
+++ b/spring-test/src/main/java/org/springframework/test/context/TestContextBootstrapper.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,25 +19,33 @@ package org.springframework.test.context;
import java.util.List;
/**
- * {@code TestContextBootstrapper} defines a strategy SPI for bootstrapping the
+ * {@code TestContextBootstrapper} defines the SPI for bootstrapping the
* Spring TestContext Framework.
*
- * Configuration
+ *
+ * Implementation Notes
+ *
*
@@ -106,7 +102,32 @@ public interface TestContextBootstrapper {
* {@code ContextLoader} class to use as as default.
*
* @return the merged context configuration, never {@code null}
+ * @see #buildTestContext()
*/
MergedContextConfiguration buildMergedContextConfiguration();
+ /**
+ * Get a list of newly instantiated {@link TestExecutionListener TestExecutionListeners}
+ * for the test class in the {@link BootstrapContext} associated with this bootstrapper.
+ *
@@ -56,6 +52,21 @@ import org.springframework.util.Assert;
* 4's {@link org.junit.AfterClass @AfterClass})
*
*
+ *