Uploaded image for project: 'Spring Framework'
  1. Spring Framework
  2. SPR-5243

Support loading WebApplicationContexts with the TestContext Framework

    Details

    • Type: New Feature
    • Status: Closed
    • Priority: Major
    • Resolution: Complete
    • Affects Version/s: 2.5 final, 3.0 GA, 3.1 GA
    • Fix Version/s: 3.2 RC1
    • Component/s: Test, Web
    • Labels:
      None
    • Last commented by a User:
      false

      Description

      Status Quo

      When the Spring TestContext Framework was introduced in Spring 2.5, it supported loading an ApplicationContext from either XML or Java Properties files. Spring 3.1 introduced support for loading an ApplicationContext from annotated classes (e.g., @Configuration classes).

      The underlying implementation for the existing support creates a GenericApplicationContext; however, a GenericApplicationContext is not suitable for testing a web application since a web application relies on an implementation of WebApplicationContext (WAC).

      In order to integration test Spring-powered web applications the Spring TestContext Framework needs to be able to load a WebApplicationContext, either from XML configuration files or from annotated classes. Furthermore, the ServletContext used by such a WAC needs to be configurable within tests, and common context hierarchies must be supported (e.g., root and dispatcher WACs in a parent-child relationship).

      Original Author's Description

      While writing some MVC integration tests, context errors were thrown when loading an XmlViewResolver and when attempting to recover command object property validation errors using the RequestContext. The reason is that each of these requires access to a WebApplicationContext, not a GenericApplicationContext which the TestContext framework makes available by default.


      Goals

      • Introduce an annotation that allows developers to configure a mock ServletContext from within integration tests.
      • Introduce SmartContextLoaders that can load WebApplicationContexts from either XML or annotated classes, using the configured mock ServletContext.
      • Provide a means for developers to access the mocks for the HttpServletRequest and HttpServletResponse objects and ensure that thread-local state in Spring MVC is kept in sync with these mock objects.
      • Ensure that metadata used to create the WebApplicationContext (e.g., ServletContext path) is used to define the unique application context cache key.

      Deliverables

      1. Implement a SmartContextLoader that loads a WebApplicationContext from XML resource locations defined via @ContextConfiguration
      2. Implement a SmartContextLoader that loads a WebApplicationContext from annotated classes defined via @ContextConfiguration
      3. Introduce a new class-level @WebAppConfiguration annotation that allows for configuration of the ServletContext base resource path, using Spring's Resource abstraction
        • see ContextMockMvcBuilder.configureWebAppRootDir() from spring-test-mvc
        • the base path must be filesystem-based by default, in contrast to the locations attribute in @ContextConfiguration which is classpath-based
        • the base path should default to "src/main/webapp", which follows the Maven convention
        • determine if @WebAppConfiguration should be inherited (i.e., annotated with @Inherited), keeping in mind that the top-level context in an EAR would not be a WAC
      4. Ensure that the two newly introduced SmartContextLoader implementations create a MockServletContext on demand (i.e., if a root WAC), when the WAC is loaded, and set the MockServletContext as the ServletContext in the application contexts that they load
      5. Set a loaded context as the ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE in the MockServletContext when context hierarchies are not used
      6. Introduce a subclass of MergedContextConfiguration specific for web apps (e.g., WebMergedContextConfiguration) that stores the ServletContext base path
        • the subclass of MCC must override equals() and hashCode() to include the metadata that uniquely identifies the resulting WAC for proper context caching
        • the buildMergedContextConfiguration() method in ContextLoaderUtils will likely need to instantiate either a standard MergedContextConfiguration or a WebMergedContextConfiguration
      7. Set up default thread local state via RequestContextHolder before each test method by implementing a new Servlet-specific TestExecutionListener
        • by using the MockServletContext already present in the WAC and by creating a MockHttpServletRequest, MockHttpServletResponse, and ServletWebRequest which will be set in the RequestContextHolder
      8. Ensure that the MockServletContext, MockHttpServletRequest, MockHttpServletResponse, and ServletWebRequest can be injected into the test instance (e.g., via @Autowired)
      9. Clean up thread locals after each test method
      10. Ensure that the Servlet-specific TestExecutionListener is configured as a default TestExecutionListener before DependencyInjectionTestExecutionListener
      11. Introduce a new web-specific DelegatingSmartContextLoader to incorporate support for the SmartContextLoader types introduced in this issue and ensure that the correct delegating loader is picked based on the presence or absence of @WebAppConfiguration
      12. Consider being able to accommodate a future request to support mocks for Spring Portlet MVC

      Pseudocode Examples


      Root WAC with Injected Mocks
      @WebAppConfiguration // path defaults to "file:src/main/webapp"
      @ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml")
      public class RootWacTests {
      
          @Autowired
          private WebApplicationContext wac;
      
          @Autowired
          private MockServletContext servletContext;
      
          @Autowired
          private MockHttpServletRequest request;
      
          @Autowired
          private MockHttpServletResponse response;
      
          @Autowired
          private MockHttpSession session;
      
          @Autowired
          private ServletWebRequest webRequest;
      
          //...
      }
      

      Further Resources

      Blogs and Custom Solutions
      Forum Discussions

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                sbrannen Sam Brannen
                Reporter:
                geoffm Geoff Metselaar
                Last updater:
                Sam Brannen
              • Votes:
                30 Vote for this issue
                Watchers:
                29 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved:
                  Days since last comment:
                  6 years, 3 weeks, 6 days ago

                  Time Tracking

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