- 
                Notifications
    You must be signed in to change notification settings 
- Fork 38.8k
Description
Geoff Metselaar opened SPR-5243 and commented
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 ServletContextfrom within integration tests.
- Introduce SmartContextLoadersthat can loadWebApplicationContextsfrom either XML or annotated classes, using the configured mockServletContext.
- Provide a means for developers to access the mocks for the HttpServletRequestandHttpServletResponseobjects 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.,ServletContextpath) is used to define the unique application context cache key.
Deliverables
-  Implement a SmartContextLoaderthat loads aWebApplicationContextfrom XML resource locations defined via@ContextConfiguration
-  Implement a SmartContextLoaderthat loads aWebApplicationContextfrom annotated classes defined via@ContextConfiguration
-  Introduce a new class-level @WebAppConfigurationannotation that allows for configuration of theServletContextbase resource path, using Spring'sResourceabstraction- see ContextMockMvcBuilder.configureWebAppRootDir()fromspring-test-mvc
- the base path must be filesystem-based by default, in contrast to the locationsattribute in@ContextConfigurationwhich is classpath-based
- the base path should default to "src/main/webapp", which follows the Maven convention
- determine if @WebAppConfigurationshould be inherited (i.e., annotated with@Inherited), keeping in mind that the top-level context in an EAR would not be a WAC
 
- see 
-  Ensure that the two newly introduced SmartContextLoaderimplementations create aMockServletContexton demand (i.e., if a root WAC), when the WAC is loaded, and set theMockServletContextas theServletContextin the application contexts that they load
-  Set a loaded context as the ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTEin theMockServletContextwhen context hierarchies are not used
-  Introduce a subclass of MergedContextConfigurationspecific for web apps (e.g.,WebMergedContextConfiguration) that stores theServletContextbase path- the subclass of MCC must override equals()andhashCode()to include the metadata that uniquely identifies the resulting WAC for proper context caching
- the buildMergedContextConfiguration()method inContextLoaderUtilswill likely need to instantiate either a standardMergedContextConfigurationor aWebMergedContextConfiguration
 
- the subclass of MCC must override 
-  Set up default thread local state via RequestContextHolderbefore each test method by implementing a new Servlet-specificTestExecutionListener- by using the MockServletContextalready present in the WAC and by creating aMockHttpServletRequest,MockHttpServletResponse, andServletWebRequestwhich will be set in theRequestContextHolder
 
- by using the 
-  Ensure that the MockServletContext,MockHttpServletRequest,MockHttpServletResponse, andServletWebRequestcan be injected into the test instance (e.g., via@Autowired)
- Clean up thread locals after each test method
-  Ensure that the Servlet-specific TestExecutionListeneris configured as a defaultTestExecutionListenerbeforeDependencyInjectionTestExecutionListener
-  Introduce a new web-specific DelegatingSmartContextLoaderto incorporate support for theSmartContextLoadertypes introduced in this issue and ensure that the correct delegating loader is picked based on the presence or absence of@WebAppConfiguration
- 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
- http://codinghood.blogspot.com/2009/01/spring-integration-testing-for-web.html
- http://jamesfarrell129.wordpress.com/2011/04/20/integration-testing-for-spring-portlet-mvc/
- http://tedyoung.me/2011/02/14/spring-mvc-integration-testing-controllers/
- http://code.google.com/p/ted-young/source/browse/trunk/blog.spring-mvc-integration-testing/
Forum Discussions
- http://forum.springframework.org/showthread.php?p=210177#post210177
- http://forum.springframework.org/showthread.php?p=210181
- http://forum.springsource.org/showthread.php?t=76744
Affects: 2.5 final, 3.0 GA, 3.1 GA
Attachments:
- web_listener_loader.zip (6.70 kB)
Issue Links:
- Provide support for context hierarchies in the TestContext Framework [SPR-5613] #10284 Provide support for context hierarchies in the TestContext Framework ("depends on")
- Support WebApplicationContext hierarchies in the TestContext Framework [SPR-9863] #14496 Support WebApplicationContext hierarchies in the TestContext Framework ("is depended on by")
- Provide support for session/request scoped beans for integration testing [SPR-4588] #9265 Provide support for session/request scoped beans for integration testing ("is depended on by")
- Document WebApplicationContext support in the TestContext Framework [SPR-9864] #14497 Document WebApplicationContext support in the TestContext Framework ("is depended on by")
- Provide a ContextLoader for WebApplicationContext [SPR-5850] #10519 Provide a ContextLoader for WebApplicationContext ("is duplicated by")
- Provide test ContextLoader for @ContextConfiguration that creates a WebApplicationContext [SPR-5399] #10072 Provide test ContextLoader for @ContextConfigurationthat creates a WebApplicationContext ("is duplicated by")
- Make Spring ContextTest annotation support WebApplicationContext [SPR-4759] #9436 Make Spring ContextTest annotation support WebApplicationContext ("is duplicated by")
- Add first class support for testing Spring MVC applications [SPR-9859] #14492 Add first class support for testing Spring MVC applications
- Introduce bootstrap strategy in the TestContext framework [SPR-9955] #14588 Introduce bootstrap strategy in the TestContext framework
- Release 1.0 spring-test-mvc [SPR-9211] #13849 Release 1.0 spring-test-mvc
- ApplicationContext fails to load in tests using Java-based config and WebMvcConfigurationSupport [SPR-9799] #14432 ApplicationContext fails to load in tests using Java-based config and WebMvcConfigurationSupport ("supersedes")
Referenced from: commits 461d99a, a281bdb, 90c5f22, 9937f84, a73280c, 21ebbb9
30 votes, 29 watchers