Spring Framework
  1. Spring Framework
  2. SPR-9011

Provide support for ApplicationContextInitializers in the TestContext framework

    Details

    • Type: New Feature New Feature
    • Status: Resolved
    • Priority: Major Major
    • Resolution: Complete
    • Affects Version/s: 3.1 GA
    • Fix Version/s: 3.2 M2
    • Component/s: Test
    • Labels:
      None
    • Last commented by a User:
      false

      Description

      Status Quo

      Starting with Spring 3.1 applications can specify contextInitializerClasses via context-param and init-param in web.xml.


      Goals

      For comprehensive testing it should be possible to re-use ApplicationContextInitializer instances in tests as well.

      This could be done at the @ContextConfiguration level by allowing an array of ACI types to be specified, and the TCF would allow each to visit the ApplicationContext at the right time.


      Deliverables

      1. Introduce a new initializers attribute in @ContextConfiguration.
        • Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>[] initializers() default {};
      2. Introduce a new inheritInitializers attribute in @ContextConfiguration.
        • boolean inheritInitializers() default true;
      3. Allow a context to be loaded solely via a custom ApplicationContextInitializer (i.e., without locations or classes)
      4. Initializers must be included in MergedContextConfiguration for determining the context cache key.
      5. Invoke initializers within existing SmartContextLoader implementations.
        • for example, in AbstractGenericContextLoader.loadContext(...) methods
        • per the contract defined in the Javadoc for ApplicationContextInitializer: ApplicationContextInitializer processors are encouraged to detect whether Spring's Ordered interface has been implemented or if the @Order annotation is present and to sort instances accordingly if so prior to invocation.
          • Collections.sort(initializerInstances, new AnnotationAwareOrderComparator());
      6. Document in Javadoc
      7. Document in the reference manual

      Pseudocode Examples

      Single Initializer
      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextConfiguration(
          locations = "/app-config.xml",
          initializers = CustomInitializer.class)
      public class ApplicationContextInitializerTests {}
      
      Multiple Initializers
      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextConfiguration(
          locations = "/app-config.xml",
          initializers = {PropertySourceInitializer.class, ProfileInitializer.class})
      public class ApplicationContextInitializerTests {}
      
      Merged Initializers
      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextConfiguration(
          classes = BaseConfig.class,
          initializers = BaseInitializer.class)
      public class BaseTest {}
      
      @ContextConfiguration(
          classes = ExtendedConfig.class,
          initializers = ExtendedInitializer.class)
      public class ExtendedTest extends BaseTest {}
      
      Overridden Initializers
      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextConfiguration(
          classes = BaseConfig.class,
          initializers = BaseInitializer.class)
      public class BaseTest {}
      
      @ContextConfiguration(
          classes = ExtendedConfig.class,
          initializers = ExtendedInitializer.class,
          inheritInitializers = false)
      public class ExtendedTest extends BaseTest {}
      
      Initializer without Locations or Classes
      // In the following example, an exception would not be thrown even
      // if no default XML file or @Configuration class is detected.
      // In other words, the initializer would be responsible for 
      // providing XML configuration files or annotated configuration
      // classes to the provided context.
      @ContextConfiguration(initializers = EntireAppInitializer.class)
      public class InitializerWithoutConfigFilesOrClassesTest extends BaseTest  {}
      

        Activity

        Hide
        Chris Beams added a comment -

        @Sam, here's a nice use case explaining why this functionality is necessary, or at least very nice to have: http://forum.springsource.org/showthread.php?123995-Registering-PropertySources-in-test-cases&p=404617#post404617

        I've asked Scott to weigh in here further to make his case.

        Show
        Chris Beams added a comment - @Sam, here's a nice use case explaining why this functionality is necessary, or at least very nice to have: http://forum.springsource.org/showthread.php?123995-Registering-PropertySources-in-test-cases&p=404617#post404617 I've asked Scott to weigh in here further to make his case.
        Hide
        Scott Frederick added a comment -

        Following Chris' blog post on Unified Property Management in Spring 3.1, I can add custom PropertySource implementations to a stand-alone or web ApplicationContext.

        I also want to be able to test how the system behaves with and without the custom PropertySource being registered. In order to do this in a test case using @ContextConfiguration, I need a way to manipulate the application context after it has been created but before it has been refreshed. Right now, as Chris suggested in the forum thread, I would need to manually create an app context in the test case instead of using @ContextConfiguration.

        Show
        Scott Frederick added a comment - Following Chris' blog post on Unified Property Management in Spring 3.1 , I can add custom PropertySource implementations to a stand-alone or web ApplicationContext. I also want to be able to test how the system behaves with and without the custom PropertySource being registered. In order to do this in a test case using @ContextConfiguration, I need a way to manipulate the application context after it has been created but before it has been refreshed. Right now, as Chris suggested in the forum thread, I would need to manually create an app context in the test case instead of using @ContextConfiguration.
        Hide
        Chris Beams added a comment -

        Sam, take a look at this post as well: http://stackoverflow.com/questions/10357985/java-spring-applicationcontext-configuration/10362527

        Not the best use case, perhaps, but it does show that folks expect ACI classes and @ContextConfiguration to work together somehow.

        Show
        Chris Beams added a comment - Sam, take a look at this post as well: http://stackoverflow.com/questions/10357985/java-spring-applicationcontext-configuration/10362527 Not the best use case, perhaps, but it does show that folks expect ACI classes and @ContextConfiguration to work together somehow.
        Hide
        Christian Nedregård added a comment - - edited

        My use case is not specific to testing. I need to be able to set the active profiles on application contexts loaded in non-web containers (e.g. Mule).
        In these cases the problem is the same as with web applications: The application context is not created by our code, but by the container.

        I would expect my ApplicationContextInitializers to be discovered and executed when defined as beans in xml or set up for classpath scanning.

        Would it be possible to generalize this issue to encompass both test-support and support for other container-created contexts?

        Show
        Christian Nedregård added a comment - - edited My use case is not specific to testing. I need to be able to set the active profiles on application contexts loaded in non-web containers (e.g. Mule). In these cases the problem is the same as with web applications: The application context is not created by our code, but by the container. I would expect my ApplicationContextInitializers to be discovered and executed when defined as beans in xml or set up for classpath scanning. Would it be possible to generalize this issue to encompass both test-support and support for other container-created contexts?
        Hide
        George P. Stathis added a comment -

        +1 for the more generalized case. We have the same problem as Scott with @ContextConfiguration in our unit tests. We have hundreds of them so, re-writing them all to manage their own ApplicationContext is definitely not something we look forward to. But besides the unit tests, we are also in the same boat as Christian; our services are distributed as a JAR, so we rely on being provided with a spring-managed context that loads settings from a properties file in the classpath. We would then use those settings to turn bean profiles or/off via spring XML configuration without having to control the Application context ourselves.

        Show
        George P. Stathis added a comment - +1 for the more generalized case. We have the same problem as Scott with @ContextConfiguration in our unit tests. We have hundreds of them so, re-writing them all to manage their own ApplicationContext is definitely not something we look forward to. But besides the unit tests, we are also in the same boat as Christian; our services are distributed as a JAR, so we rely on being provided with a spring-managed context that loads settings from a properties file in the classpath. We would then use those settings to turn bean profiles or/off via spring XML configuration without having to control the Application context ourselves.
        Hide
        Sam Brannen added a comment - - edited

        Christian and George,

        I would expect my ApplicationContextInitializers to be discovered and executed when defined as beans in xml or set up for classpath scanning.

        It is not feasible to have an ApplicationContextInitializer (ACI) defined as a Spring bean since an ACI should not reside in the ApplicationContext which it initializes.

        Would it be possible to generalize this issue to encompass both test-support and support for other container-created contexts?

        No, this issue is dedicated to providing support for configuring ACIs in the TestContext framework (e.g., via @ContextConfiguration).

        If you would like to see support for configuring ACIs in other deployment environments, please create a separate JIRA issue for that specific use case.

        Regards,

        Sam

        Show
        Sam Brannen added a comment - - edited Christian and George, I would expect my ApplicationContextInitializers to be discovered and executed when defined as beans in xml or set up for classpath scanning. It is not feasible to have an ApplicationContextInitializer ( ACI ) defined as a Spring bean since an ACI should not reside in the ApplicationContext which it initializes. Would it be possible to generalize this issue to encompass both test-support and support for other container-created contexts? No, this issue is dedicated to providing support for configuring ACIs in the TestContext framework (e.g., via @ContextConfiguration ). If you would like to see support for configuring ACIs in other deployment environments, please create a separate JIRA issue for that specific use case. Regards, Sam
        Hide
        Sam Brannen added a comment -

        If you are watching this issue, please feel free to participate in the discussion regarding the proposed Deliverables and the corresponding Pseudocode Examples listed in this issue's Description.

        Thanks!

        Sam

        Show
        Sam Brannen added a comment - If you are watching this issue, please feel free to participate in the discussion regarding the proposed Deliverables and the corresponding Pseudocode Examples listed in this issue's Description . Thanks! Sam
        Hide
        Sam Brannen added a comment - - edited

        Completed as described in the message for GitHub commit 58daeea1e2d7f3688057e0131cba5291a6f70fc2:

        Support ApplicationContextInitializers in the TCF

        Starting with Spring 3.1 applications can specify
        contextInitializerClasses via context-param and init-param in web.xml;
        however, there is currently no way to have such initializers invoked in
        integration testing scenarios without writing a custom
        SmartContextLoader. For comprehensive integration testing it should
        therefore be possible to re-use ApplicationContextInitializers in the
        Spring TestContext Framework as well.

        This commit makes this possible at the @ContextConfiguration level by
        allowing an array of ACI types to be specified, and the out-of-the-box
        SmartContextLoader implementations invoke the declared initializers at
        the appropriate time.

        • Added initializers and inheritInitializers attributes to
          @ContextConfiguration.
        • Introduced support for ApplicationContextInitializers in
          ContextConfigurationAttributes, MergedContextConfiguration, and
          ContextLoaderUtils.
        • MergedContextConfiguration stores context initializer classes as a
          Set and incorporates them into the implementations of hashCode() and
          equals() for proper context caching.
        • ApplicationContextInitializers are invoked in the new
          prepareContext(GenericApplicationContext, MergedContextConfiguration)
          method in AbstractGenericContextLoader, and ordering declared via the
          Ordered interface and @Order annotation is honored.
        • Updated DelegatingSmartContextLoader to support initializers.
          Specifically, a test class may optionally declare neither XML
          configuration files nor annotated classes and instead declare only
          application context initializers. In such cases, an attempt will
          still be made to detect defaults, but their absence will not result
          an an exception.
        • Documented support for application context initializers in Javadoc
          and in the testing chapter of the reference manual.
        Show
        Sam Brannen added a comment - - edited Completed as described in the message for GitHub commit 58daeea1e2d7f3688057e0131cba5291a6f70fc2 : Support ApplicationContextInitializers in the TCF Starting with Spring 3.1 applications can specify contextInitializerClasses via context-param and init-param in web.xml; however, there is currently no way to have such initializers invoked in integration testing scenarios without writing a custom SmartContextLoader. For comprehensive integration testing it should therefore be possible to re-use ApplicationContextInitializers in the Spring TestContext Framework as well. This commit makes this possible at the @ContextConfiguration level by allowing an array of ACI types to be specified, and the out-of-the-box SmartContextLoader implementations invoke the declared initializers at the appropriate time. Added initializers and inheritInitializers attributes to @ContextConfiguration. Introduced support for ApplicationContextInitializers in ContextConfigurationAttributes, MergedContextConfiguration, and ContextLoaderUtils. MergedContextConfiguration stores context initializer classes as a Set and incorporates them into the implementations of hashCode() and equals() for proper context caching. ApplicationContextInitializers are invoked in the new prepareContext(GenericApplicationContext, MergedContextConfiguration) method in AbstractGenericContextLoader, and ordering declared via the Ordered interface and @Order annotation is honored. Updated DelegatingSmartContextLoader to support initializers. Specifically, a test class may optionally declare neither XML configuration files nor annotated classes and instead declare only application context initializers. In such cases, an attempt will still be made to detect defaults, but their absence will not result an an exception. Documented support for application context initializers in Javadoc and in the testing chapter of the reference manual.

          People

          • Assignee:
            Sam Brannen
            Reporter:
            Rossen Stoyanchev
            Last updater:
            Sam Brannen
          • Votes:
            11 Vote for this issue
            Watchers:
            16 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:
              Days since last comment:
              1 year, 35 weeks, 1 day ago

              Time Tracking

              Estimated:
              Original Estimate - Not Specified
              Not Specified
              Remaining:
              Remaining Estimate - 0d
              0d
              Logged:
              Time Spent - 4.5d
              4.5d