From 186abcb054303c30620ecf3620be25e7b9cf7e29 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Sat, 18 Apr 2015 23:06:57 +0200 Subject: [PATCH] Introduce buildTestContext() in TestContextBootstrapper This commit moves the responsibility of building a TestContext from the TestContextManager to a TestContextBootstrapper. In addition, DefaultTestContext is now a public class residing in the "support" subpackage. Issue: SPR-12683 --- .../test/context/BootstrapContext.java | 4 +- .../test/context/TestContextBootstrapper.java | 64 +++++++++++-------- .../test/context/TestContextManager.java | 29 ++++++--- .../AbstractTestContextBootstrapper.java | 16 +++++ .../{ => support}/DefaultTestContext.java | 54 ++++++++++------ .../test/context/TestContextTestUtils.java | 8 +-- 6 files changed, 115 insertions(+), 60 deletions(-) rename spring-test/src/main/java/org/springframework/test/context/{ => support}/DefaultTestContext.java (55%) 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 fa12a797e3..13c311fa79 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,7 +28,7 @@ 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(); 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 7252657f42..ae81c9ec4a 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. @@ -29,15 +29,14 @@ import java.util.List; * *

The {@link TestContextManager} uses a {@code TestContextBootstrapper} 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. * *

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 +58,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 getTestExecutionListeners(); + TestContext buildTestContext(); /** * Build the {@linkplain MergedContextConfiguration merged context configuration} @@ -94,9 +78,12 @@ public interface TestContextBootstrapper { *

  • Active bean definition profiles declared via {@link ActiveProfiles @ActiveProfiles}
  • *
  • {@linkplain org.springframework.context.ApplicationContextInitializer * Context initializers} declared via {@link ContextConfiguration#initializers}
  • + *
  • Test property sources declared via {@link TestPropertySource @TestPropertySource}
  • * *

    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: *

      @@ -106,7 +93,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. + *

    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 getTestExecutionListeners(); + } diff --git a/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java b/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java index 9b7fa95735..b0b93195bb 100644 --- a/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java +++ b/spring-test/src/main/java/org/springframework/test/context/TestContextManager.java @@ -28,15 +28,11 @@ import org.springframework.util.Assert; /** * {@code TestContextManager} is the main entry point into the Spring - * TestContext Framework, which provides 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. + * TestContext Framework. * *

    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 @@ -90,7 +101,7 @@ public class TestContextManager { CacheAwareContextLoaderDelegate cacheAwareContextLoaderDelegate = new DefaultCacheAwareContextLoaderDelegate(); BootstrapContext bootstrapContext = new DefaultBootstrapContext(testClass, cacheAwareContextLoaderDelegate); TestContextBootstrapper testContextBootstrapper = BootstrapUtils.resolveTestContextBootstrapper(bootstrapContext); - this.testContext = new DefaultTestContext(testContextBootstrapper); + this.testContext = testContextBootstrapper.buildTestContext(); registerTestExecutionListeners(testContextBootstrapper.getTestExecutionListeners()); } @@ -190,7 +201,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 + "]"); } @@ -271,7 +282,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 709c82e972..d538777e57 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 @@ -44,6 +44,7 @@ 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; @@ -93,6 +94,21 @@ public abstract class AbstractTestContextBootstrapper implements TestContextBoot return this.bootstrapContext; } + /** + * Build a new {@link DefaultTestContext} using the {@linkplain Class test class} + * and {@link CacheAwareContextLoaderDelegate} in the {@link BootstrapContext} + * associated with this bootstrapper and by delegating to + * {@link #buildMergedContextConfiguration()}. + *

    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(bootstrapContext.getTestClass(), buildMergedContextConfiguration(), + bootstrapContext.getCacheAwareContextLoaderDelegate()); + } + /** * {@inheritDoc} */ 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 eac358e8e5..bd730b3254 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/TestContextTestUtils.java b/spring-test/src/test/java/org/springframework/test/context/TestContextTestUtils.java index 3f1eb86edf..da43525bc0 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. @@ -28,12 +28,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(); } }