Skip to content

Support loading WebApplicationContexts with the TestContext Framework [SPR-5243] #9917

Closed
@spring-projects-issues

Description

@spring-projects-issues

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

Affects: 2.5 final, 3.0 GA, 3.1 GA

Attachments:

Issue Links:

Referenced from: commits 461d99a, a281bdb, 90c5f22, 9937f84, a73280c, 21ebbb9

30 votes, 29 watchers

Metadata

Metadata

Assignees

Labels

has: votes-jiraIssues migrated from JIRA with more than 10 votes at the time of importin: testIssues in the test modulein: webIssues in web modules (web, webmvc, webflux, websocket)type: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions