Spring Framework
  1. Spring Framework
  2. SPR-5613

Provide support for context hierarchies in the TestContext Framework

    Details

    • Type: New Feature New Feature
    • Status: Closed
    • Priority: Major Major
    • Resolution: Complete
    • Affects Version/s: 2.5.6
    • Fix Version/s: 3.2.2
    • Component/s: Test
    • Labels:
      None
    • Last commented by a User:
      false

      Description

      Status Quo

      Currently the TestContext framework supports creating only flat, non-hierarchical contexts. There is no easy way to create contexts with parent-child relationships.


      Goals

      • Add support for creating a test context with a parent context.
      • Configuration in the TestContext framework should allow for any number of levels in the context hierarchy.
      • Different levels in the context hierarchy may have different types of contexts (e.g., XML or annotation classes).
      Example Hierarchies
      • Root WebApplicationContext <-- Dispatcher WebApplicationContext
      • EAR ApplicationContext <-- Root WebApplicationContext <-- Dispatcher WebApplicationContext

      Deliverables

      1. Introduce a new @ContextHierarchy annotation that can contain nested @ContextConfiguration declarations
      2. Introduce a new name attribute in @ContextConfiguration that can be used for merging or overriding named @ContextConfiguration elements in the context hierarchy
      3. Assemble the context hierarchy for a given test class based on the levels defined in @ContextHierarchy in a single test class
      4. Assemble the context hierarchy for a given test class based on the levels defined in @ContextHierarchy and @ContextConfiguration within the test class hierarchy
      5. Support merging of configuration files or classes at any level in the hierarchy via the name attribute of @ContextConfiguration
      6. Support overriding of configuration files or classes at any level in the hierarchy via the name and inheritLocations attributes of @ContextConfiguration
      7. Cache each individual context within a hierarchy
      8. Support dirtying of the context hierarchy, including dirtying of any other affected hierarchies that share a common ancestor context
      9. Detect default XML resource location or default annotated classes, if feasible

      Implementation Considerations

      • MergedContextConfiguration should have a reference to the parent MergedContextConfiguration (i.e., a private final field)
      • MergedContextConfiguration should provide a public ApplicationContext getParentApplicationContext() method that SmartContextLoader instances can use to retrieve and set the parent ApplicationContext
        • MergedContextConfiguration will need an internal reference to the context cache (or at least a way to retrieve the parent context from the cache)

      Pseudocode Examples


      Single Test Class with Context Hierarchy
      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextHierarchy({
      	@ContextConfiguration("parent.xml"),
      	@ContextConfiguration("child.xml")
      })
      public class AppCtxHierarchyTests {}
      
      Resulting Hierarchies
      +------------+
      | parent.xml |
      +------------+
            ^
            |
      +------------+
      | child.xml  |
      +------------+
      

      Class Hierarchy with Implicit Parent Context
      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextHierarchy(@ContextConfiguration(classes = AppConfig.class))
      public abstract class AbstractTests {}
      
      @ContextHierarchy(@ContextConfiguration("/A-config.xml"))
      public class A extends AbstractTests {}
      
      @ContextHierarchy(@ContextConfiguration("/B-config.xml"))
      public class B extends AbstractTests {}
      
      Resulting Hierarchies
      +-----------------+
      | AppConfig.class |
      +-----------------+
               ^
               |
      +-----------------+
      |  A-config.xml   |
      +-----------------+
      
      +-----------------+
      | AppConfig.class |
      +-----------------+
               ^
               |
      +-----------------+
      |  B-config.xml   |
      +-----------------+
      

      Class Hierarchy with Bare @ContextConfiguration in a Superclass

      In this scenario it is assumed that AbstractTests existed prior to the introduction of @ContextHierarchy support and that the author does not wish to (or cannot) modify AbstractTests to declare a @ContextHierarchy.

      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextConfiguration(classes = AppConfig.class)
      public abstract class AbstractTests {}
      
      @ContextHierarchy(@ContextConfiguration("/A-config.xml"))
      public class A extends AbstractTests {}
      
      Resulting Hierarchies
      +-----------------+
      | AppConfig.class |
      +-----------------+
               ^
               |
      +-----------------+
      |  A-config.xml   |
      +-----------------+
      

      Class Hierarchy with Bare @ContextConfiguration in a Subclass

      This scenario is not necessarily recommended, but it should still be supported for the sake of consistency.

      Ideally, if the author of a test class knows that the context for the test class should take part in a context hierarchy, then the author should explicitly declare @ContextConfiguration within @ContextHierarchy.

      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextHierarchy(@ContextConfiguration(classes = AppConfig.class))
      public abstract class AbstractTests {}
      
      @ContextConfiguration("/A-config.xml")
      public class A extends AbstractTests {}
      
      Resulting Hierarchies
      +-----------------+
      | AppConfig.class |
      +-----------------+
               ^
               |
      +-----------------+
      |  A-config.xml   |
      +-----------------+
      

      Class Hierarchy with Merged Configuration
      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextHierarchy({
          @ContextConfiguration(name = "parent", locations = "/app-config.xml"),
          @ContextConfiguration(name = "child",  locations = "/user-config.xml")
      })
      public class BaseTests {}
      
      @ContextHierarchy(
          @ContextConfiguration(name = "child",  locations = "/order-config.xml")
      )
      public class ExtendedTests extends BaseTests {}
      
      Resulting Hierarchies
      +----------------+
      | app-config.xml |
      +----------------+
              ^
              |
      +-----------------+
      | user-config.xml |
      +-----------------+
      
                +----------------+
                | app-config.xml |
                +----------------+
                        ^
                        |
      +-----------------------------------+
      | user-config.xml, order-config.xml |
      +-----------------------------------+
      

      Class Hierarchy with Overridden Configuration
      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextHierarchy({
          @ContextConfiguration(name = "parent", locations = "/app-config.xml"),
          @ContextConfiguration(name = "child",  locations = "/user-config.xml")
      })
      public class BaseTests {}
      
      @ContextHierarchy(
          @ContextConfiguration(name = "child",  locations = "/test-user-config.xml", inheritLocations=false)
      )
      public class ExtendedTests extends BaseTests {}
      
      Resulting Hierarchies
      +----------------+
      | app-config.xml |
      +----------------+
              ^
              |
      +-----------------+
      | user-config.xml |
      +-----------------+
      
         +----------------+
         | app-config.xml |
         +----------------+
                 ^
                 |
      +----------------------+
      | test-user-config.xml |
      +----------------------+
      

        Issue Links

          Activity

          Hide
          Tadaya Tsuyukubo added a comment -

          Hi Sam,

          I was looking your sneakpeak code and implementing the @DirtiesContext support for context hierarchy.

          I was thinking the usage to be like this:

          @RunWith(SpringJUnit4ClassRunner.class)
          @ContextHierarchy({
          	@ContextConfiguration(name="foo", locations="/foo-config.xml"),
          	@ContextConfiguration(name="bar", locations="/bar-config.xml"), 
          	@ContextConfiguration(name="baz", locations="/baz-config.xml")
          })
          public class AppCtxHierarchyTests {
          	@Test
          	@DirtiesContext("bar")
          	public void testTaintsBar(){...}
          }
          

          The above example would mark "bar" and it's child contexts such as "baz" as dirty.

          If this is the expected behavior, I'll finish up the implementation and send a pull request.

          Thanks,

          Show
          Tadaya Tsuyukubo added a comment - Hi Sam, I was looking your sneakpeak code and implementing the @DirtiesContext support for context hierarchy. I was thinking the usage to be like this: @RunWith(SpringJUnit4ClassRunner.class) @ContextHierarchy({ @ContextConfiguration(name= "foo" , locations= "/foo-config.xml" ), @ContextConfiguration(name= "bar" , locations= "/bar-config.xml" ), @ContextConfiguration(name= "baz" , locations= "/baz-config.xml" ) }) public class AppCtxHierarchyTests { @Test @DirtiesContext( "bar" ) public void testTaintsBar(){...} } The above example would mark "bar" and it's child contexts such as "baz" as dirty. If this is the expected behavior, I'll finish up the implementation and send a pull request. Thanks,
          Hide
          Tadaya Tsuyukubo added a comment -

          Hi Sam,
          I wrote "@DirtiesContext context hierarchy support" on top of your SPR-5613 branch based on the spec in my comment above.

          pull request is here:
          https://github.com/sbrannen/spring-framework/pull/1

          Thanks,

          Show
          Tadaya Tsuyukubo added a comment - Hi Sam, I wrote "@DirtiesContext context hierarchy support" on top of your SPR-5613 branch based on the spec in my comment above. pull request is here: https://github.com/sbrannen/spring-framework/pull/1 Thanks,
          Hide
          Sam Brannen added a comment -

          Tadaya Tsuyukubo, thanks for submitting the pull request!

          Unfortunately, using the names of context hierarchy levels to clear the context cache is fundamentally flawed. Please see my comments in your pull request for details.

          Regards,

          Sam

          Show
          Sam Brannen added a comment - Tadaya Tsuyukubo , thanks for submitting the pull request! Unfortunately, using the names of context hierarchy levels to clear the context cache is fundamentally flawed. Please see my comments in your pull request for details. Regards, Sam
          Hide
          Sam Brannen added a comment -
          Show
          Sam Brannen added a comment - Submitted pull request: https://github.com/SpringSource/spring-framework/pull/247
          Hide
          Sam Brannen added a comment -

          Completed as described in the comments for GitHub commit 4c5d771764:

          Provide support for context hierarchies in the TCF

          Prior to this commit the Spring TestContext Framework supported creating
          only flat, non-hierarchical contexts. There was no easy way to create
          contexts with parent-child relationships.

          This commit addresses this issue by introducing a new @ContextHierarchy
          annotation that can be used in conjunction with @ContextConfiguration
          for declaring hierarchies of application contexts, either within a
          single test class or within a test class hierarchy. In addition,
          @DirtiesContext now supports a new 'hierarchyMode' attribute for
          controlling context cache clearing for context hierarchies.

          • Introduced a new @ContextHierarchy annotation.
          • Introduced 'name' attribute in @ContextConfiguration.
          • Introduced 'name' property in ContextConfigurationAttributes.
          • TestContext is now aware of @ContextHierarchy in addition to
            @ContextConfiguration.
          • Introduced findAnnotationDeclaringClassForTypes() in AnnotationUtils.
          • Introduced resolveContextHierarchyAttributes() in ContextLoaderUtils.
          • Introduced buildContextHierarchyMap() in ContextLoaderUtils.
          • @ContextConfiguration and @ContextHierarchy may not be used as
            top-level, class-level annotations simultaneously.
          • Introduced reference to the parent configuration in
            MergedContextConfiguration and WebMergedContextConfiguration.
          • Introduced overloaded buildMergedContextConfiguration() methods in
            ContextLoaderUtils in order to handle context hierarchies separately
            from conventional, non-hierarchical contexts.
          • Introduced hashCode() and equals() in ContextConfigurationAttributes.
          • ContextLoaderUtils ensures uniqueness of @ContextConfiguration
            elements within a single @ContextHierarchy declaration.
          • Introduced CacheAwareContextLoaderDelegate that can be used for
            loading contexts with transparent support for interacting with the
            context cache – for example, for retrieving the parent application
            context in a context hierarchy.
          • TestContext now delegates to CacheAwareContextLoaderDelegate for
            loading contexts.
          • Introduced getParentApplicationContext() in MergedContextConfiguration
          • The loadContext(MergedContextConfiguration) methods in
            AbstractGenericContextLoader and AbstractGenericWebContextLoader now
            set the parent context as appropriate.
          • Introduced 'hierarchyMode' attribute in @DirtiesContext with a
            corresponding HierarchyMode enum that defines EXHAUSTIVE and
            CURRENT_LEVEL cache removal modes.
          • ContextCache now internally tracks the relationships between contexts
            that make up a context hierarchy. Furthermore, when a context is
            removed, if it is part of a context hierarchy all corresponding
            contexts will be removed from the cache according to the supplied
            HierarchyMode.
          • AbstractGenericWebContextLoader will set a loaded context as the
            ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE in the MockServletContext when
            context hierarchies are used if the context has no parent or if the
            context has a parent that is not a WAC.
          • Where appropriate, updated Javadoc to refer to the
            ServletTestExecutionListener, which was introduced in 3.2.0.
          • Updated Javadoc to avoid and/or suppress warnings in spring-test.
          • Suppressed remaining warnings in code in spring-test.
          Show
          Sam Brannen added a comment - Completed as described in the comments for GitHub commit 4c5d771764 : Provide support for context hierarchies in the TCF Prior to this commit the Spring TestContext Framework supported creating only flat, non-hierarchical contexts. There was no easy way to create contexts with parent-child relationships. This commit addresses this issue by introducing a new @ContextHierarchy annotation that can be used in conjunction with @ContextConfiguration for declaring hierarchies of application contexts, either within a single test class or within a test class hierarchy. In addition, @DirtiesContext now supports a new 'hierarchyMode' attribute for controlling context cache clearing for context hierarchies. Introduced a new @ContextHierarchy annotation. Introduced 'name' attribute in @ContextConfiguration. Introduced 'name' property in ContextConfigurationAttributes. TestContext is now aware of @ContextHierarchy in addition to @ContextConfiguration. Introduced findAnnotationDeclaringClassForTypes() in AnnotationUtils. Introduced resolveContextHierarchyAttributes() in ContextLoaderUtils. Introduced buildContextHierarchyMap() in ContextLoaderUtils. @ContextConfiguration and @ContextHierarchy may not be used as top-level, class-level annotations simultaneously. Introduced reference to the parent configuration in MergedContextConfiguration and WebMergedContextConfiguration. Introduced overloaded buildMergedContextConfiguration() methods in ContextLoaderUtils in order to handle context hierarchies separately from conventional, non-hierarchical contexts. Introduced hashCode() and equals() in ContextConfigurationAttributes. ContextLoaderUtils ensures uniqueness of @ContextConfiguration elements within a single @ContextHierarchy declaration. Introduced CacheAwareContextLoaderDelegate that can be used for loading contexts with transparent support for interacting with the context cache – for example, for retrieving the parent application context in a context hierarchy. TestContext now delegates to CacheAwareContextLoaderDelegate for loading contexts. Introduced getParentApplicationContext() in MergedContextConfiguration The loadContext(MergedContextConfiguration) methods in AbstractGenericContextLoader and AbstractGenericWebContextLoader now set the parent context as appropriate. Introduced 'hierarchyMode' attribute in @DirtiesContext with a corresponding HierarchyMode enum that defines EXHAUSTIVE and CURRENT_LEVEL cache removal modes. ContextCache now internally tracks the relationships between contexts that make up a context hierarchy. Furthermore, when a context is removed, if it is part of a context hierarchy all corresponding contexts will be removed from the cache according to the supplied HierarchyMode. AbstractGenericWebContextLoader will set a loaded context as the ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE in the MockServletContext when context hierarchies are used if the context has no parent or if the context has a parent that is not a WAC. Where appropriate, updated Javadoc to refer to the ServletTestExecutionListener, which was introduced in 3.2.0. Updated Javadoc to avoid and/or suppress warnings in spring-test. Suppressed remaining warnings in code in spring-test.

            People

            • Assignee:
              Sam Brannen
              Reporter:
              Tomasz Wysocki
              Last updater:
              Sam Brannen
            • Votes:
              10 Vote for this issue
              Watchers:
              11 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:
                Days since last comment:
                1 year, 6 weeks, 6 days ago

                Time Tracking

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