Support loading WebApplicationContexts in the TCF
Prior to this commit, the Spring TestContext Framework only supported loading an ApplicationContext in integration tests from either XML or Java Properties files (since Spring 2.5), and Spring 3.1 introduced support for loading an ApplicationContext in integration tests from annotated classes (e.g., @Configuration classes). All of the ContextLoader implementations used to provide this support load a GenericApplicationContext. However, a GenericApplicationContext is not suitable for testing a web application since a web application relies on an implementation of WebApplicationContext (WAC). This commit makes it possible to integration test Spring-powered web applications by adding the following functionality to the Spring TestContext Framework. - Introduced AbstractGenericWebContextLoader and two concrete subclasses: - XmlWebContextLoader - AnnotationConfigWebContextLoader - Pulled up prepareContext(context, mergedConfig) from AbstractGenericContextLoader into AbstractContextLoader to allow it to be shared across web and non-web context loaders. - Introduced AnnotationConfigContextLoaderUtils and refactored AnnotationConfigContextLoader accordingly. These utils are also used by AnnotationConfigWebContextLoader. - Introduced a new @WebAppConfiguration annotation to denote that the ApplicationContext loaded for a test should be a WAC and to configure the base resource path for the root directory of a web application. - Introduced WebMergedContextConfiguration which extends MergedContextConfiguration with support for a baseResourcePath for the root directory of a web application. - ContextLoaderUtils.buildMergedContextConfiguration() now builds a WebMergedContextConfiguration instead of a standard MergedContextConfiguration if @WebAppConfiguration is present on the test class. - Introduced a configureWebResources() method in AbstractGenericWebContextLoader that is responsible for creating a MockServletContext with a proper ResourceLoader for the resourceBasePath configured in the WebMergedContextConfiguration. The resulting mock ServletContext is set in the WAC, and the WAC is stored as the Root WAC in the ServletContext. - Introduced a WebTestExecutionListener that sets up default thread local state via RequestContextHolder before each test method by using the MockServletContext already present in the WAC and by creating a MockHttpServletRequest, MockHttpServletResponse, and ServletWebRequest that is set in the RequestContextHolder. WTEL also ensures that the MockHttpServletResponse and ServletWebRequest can be injected into the test instance (e.g., via @Autowired) and cleans up thread locals after each test method. - WebTestExecutionListener is configured as a default TestExecutionListener before DependencyInjectionTestExecutionListener - Extracted AbstractDelegatingSmartContextLoader from DelegatingSmartContextLoader and introduced a new WebDelegatingSmartContextLoader. - ContextLoaderUtils now selects the default delegating ContextLoader class name based on the presence of @WebAppConfiguration on the test class. - Tests in the spring-test-mvc module no longer use a custom ContextLoader to load a WebApplicationContext. Instead, they now rely on new core functionality provided in this commit. Issue: SPR-5243
This commit is contained in:
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.test.web.mock.servlet.samples.context;
|
||||
|
||||
import javax.servlet.RequestDispatcher;
|
||||
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.FileSystemResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.mock.web.MockRequestDispatcher;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.support.AbstractContextLoader;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||
|
||||
/**
|
||||
* This class is here temporarily until the TestContext framework provides
|
||||
* support for WebApplicationContext yet:
|
||||
*
|
||||
* https://jira.springsource.org/browse/SPR-5243
|
||||
*
|
||||
* <p>After that this class will no longer be needed. It's provided here as an example
|
||||
* and to serve as a temporary solution.
|
||||
*/
|
||||
public class GenericWebContextLoader extends AbstractContextLoader {
|
||||
protected final MockServletContext servletContext;
|
||||
|
||||
public GenericWebContextLoader(String warRootDir, boolean isClasspathRelative) {
|
||||
ResourceLoader resourceLoader = isClasspathRelative ? new DefaultResourceLoader() : new FileSystemResourceLoader();
|
||||
this.servletContext = initServletContext(warRootDir, resourceLoader);
|
||||
}
|
||||
|
||||
private MockServletContext initServletContext(String warRootDir, ResourceLoader resourceLoader) {
|
||||
return new MockServletContext(warRootDir, resourceLoader) {
|
||||
// Required for DefaultServletHttpRequestHandler...
|
||||
public RequestDispatcher getNamedDispatcher(String path) {
|
||||
return (path.equals("default")) ? new MockRequestDispatcher(path) : super.getNamedDispatcher(path);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public ApplicationContext loadContext(MergedContextConfiguration mergedConfig) throws Exception {
|
||||
GenericWebApplicationContext context = new GenericWebApplicationContext();
|
||||
context.getEnvironment().setActiveProfiles(mergedConfig.getActiveProfiles());
|
||||
prepareContext(context);
|
||||
loadBeanDefinitions(context, mergedConfig);
|
||||
return context;
|
||||
}
|
||||
|
||||
public ApplicationContext loadContext(String... locations) throws Exception {
|
||||
// should never be called
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
protected void prepareContext(GenericWebApplicationContext context) {
|
||||
this.servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
|
||||
context.setServletContext(this.servletContext);
|
||||
}
|
||||
|
||||
protected void loadBeanDefinitions(GenericWebApplicationContext context, String[] locations) {
|
||||
new XmlBeanDefinitionReader(context).loadBeanDefinitions(locations);
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
context.refresh();
|
||||
context.registerShutdownHook();
|
||||
}
|
||||
|
||||
protected void loadBeanDefinitions(GenericWebApplicationContext context, MergedContextConfiguration mergedConfig) {
|
||||
new AnnotatedBeanDefinitionReader(context).register(mergedConfig.getClasses());
|
||||
loadBeanDefinitions(context, mergedConfig.getLocations());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getResourceSuffix() {
|
||||
return "-context.xml";
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,8 @@
|
||||
|
||||
package org.springframework.test.web.mock.servlet.samples.context;
|
||||
|
||||
import static org.springframework.test.web.mock.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.mock.servlet.result.MockMvcResultMatchers.forwardedUrl;
|
||||
import static org.springframework.test.web.mock.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.test.web.mock.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.mock.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -26,21 +25,20 @@ import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.mock.servlet.MockMvc;
|
||||
import org.springframework.test.web.mock.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.context.ContextLoader;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
/**
|
||||
* Tests with Java configuration.
|
||||
*
|
||||
* The TestContext framework doesn't support WebApplicationContext yet:
|
||||
* https://jira.springsource.org/browse/SPR-5243
|
||||
*
|
||||
* A custom {@link ContextLoader} is used to load the WebApplicationContext.
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(loader=WebContextLoader.class, classes={WebConfig.class})
|
||||
@WebAppConfiguration("src/test/resources/META-INF/web-resources")
|
||||
@ContextConfiguration(classes = WebConfig.class)
|
||||
public class JavaTestContextTests {
|
||||
|
||||
@Autowired
|
||||
@@ -48,6 +46,7 @@ public class JavaTestContextTests {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
||||
@@ -55,9 +54,9 @@ public class JavaTestContextTests {
|
||||
|
||||
@Test
|
||||
public void tilesDefinitions() throws Exception {
|
||||
this.mockMvc.perform(get("/"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(forwardedUrl("/WEB-INF/layouts/standardLayout.jsp"));
|
||||
this.mockMvc.perform(get("/"))//
|
||||
.andExpect(status().isOk())//
|
||||
.andExpect(forwardedUrl("/WEB-INF/layouts/standardLayout.jsp"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.test.web.mock.servlet.samples.context;
|
||||
|
||||
import static org.springframework.test.web.mock.servlet.request.MockMvcRequestBuilders.get;
|
||||
@@ -34,6 +35,7 @@ import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.mock.servlet.MockMvc;
|
||||
import org.springframework.test.web.mock.servlet.MvcResult;
|
||||
import org.springframework.test.web.mock.servlet.ResultMatcher;
|
||||
@@ -44,32 +46,25 @@ import org.springframework.web.context.WebApplicationContext;
|
||||
/**
|
||||
* Basic example that includes Spring Security configuration.
|
||||
*
|
||||
* <p>Note that currently there are no {@link ResultMatcher}' built specifically
|
||||
* for asserting the Spring Security context. However, it's quite easy to put
|
||||
* them together as shown below and Spring Security extensions will become
|
||||
* available in the near future.
|
||||
* <p>Note that currently there are no {@linkplain ResultMatcher ResultMatchers}
|
||||
* built specifically for asserting the Spring Security context. However, it's
|
||||
* quite easy to put them together as shown below, and Spring Security extensions
|
||||
* will become available in the near future.
|
||||
*
|
||||
* <p>This also demonstrates a custom {@link RequestPostProcessor} which authenticates
|
||||
* a user to a particular {@link HttpServletRequest}.
|
||||
*
|
||||
* <p>Also see the Javadoc of {@link GenericWebContextLoader}, a class that
|
||||
* provides temporary support for loading WebApplicationContext by extending
|
||||
* the TestContext framework.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sam Brannen
|
||||
* @see SecurityRequestPostProcessors
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(
|
||||
loader=WebContextLoader.class,
|
||||
value={
|
||||
"classpath:org/springframework/test/web/mock/servlet/samples/context/security.xml",
|
||||
"classpath:org/springframework/test/web/mock/servlet/samples/servlet-context.xml"
|
||||
})
|
||||
@WebAppConfiguration("src/test/resources/META-INF/web-resources")
|
||||
@ContextConfiguration({ "security.xml", "../servlet-context.xml" })
|
||||
public class SpringSecurityTests {
|
||||
|
||||
private static String SEC_CONTEXT_ATTR = HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;
|
||||
private static final String SEC_CONTEXT_ATTR = HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;
|
||||
|
||||
@Autowired
|
||||
private FilterChainProxy springSecurityFilterChain;
|
||||
@@ -79,57 +74,67 @@ public class SpringSecurityTests {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
|
||||
.addFilters(this.springSecurityFilterChain).build();
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)//
|
||||
.addFilters(this.springSecurityFilterChain)//
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requiresAuthentication() throws Exception {
|
||||
mockMvc.perform(get("/user"))
|
||||
.andExpect(redirectedUrl("http://localhost/spring_security_login"));
|
||||
mockMvc.perform(get("/user")).//
|
||||
andExpect(redirectedUrl("http://localhost/spring_security_login"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accessGranted() throws Exception {
|
||||
this.mockMvc.perform(get("/").with(userDeatilsService("user")))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(forwardedUrl("/WEB-INF/layouts/standardLayout.jsp"));
|
||||
this.mockMvc.perform(get("/").//
|
||||
with(userDeatilsService("user"))).//
|
||||
andExpect(status().isOk()).//
|
||||
andExpect(forwardedUrl("/WEB-INF/layouts/standardLayout.jsp"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accessDenied() throws Exception {
|
||||
this.mockMvc.perform(get("/").with(user("user").roles("DENIED")))
|
||||
.andExpect(status().isForbidden());
|
||||
this.mockMvc.perform(get("/")//
|
||||
.with(user("user").roles("DENIED")))//
|
||||
.andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void userAuthenticates() throws Exception {
|
||||
final String username = "user";
|
||||
mockMvc.perform(post("/j_spring_security_check").param("j_username", username).param("j_password", "password"))
|
||||
.andExpect(redirectedUrl("/"))
|
||||
.andExpect(new ResultMatcher() {
|
||||
public void match(MvcResult mvcResult) throws Exception {
|
||||
HttpSession session = mvcResult.getRequest().getSession();
|
||||
SecurityContext securityContext = (SecurityContext) session.getAttribute(SEC_CONTEXT_ATTR);
|
||||
Assert.assertEquals(securityContext.getAuthentication().getName(), username);
|
||||
}
|
||||
});
|
||||
mockMvc.perform(post("/j_spring_security_check").//
|
||||
param("j_username", username).//
|
||||
param("j_password", "password")).//
|
||||
andExpect(redirectedUrl("/")).//
|
||||
andExpect(new ResultMatcher() {
|
||||
|
||||
public void match(MvcResult mvcResult) throws Exception {
|
||||
HttpSession session = mvcResult.getRequest().getSession();
|
||||
SecurityContext securityContext = (SecurityContext) session.getAttribute(SEC_CONTEXT_ATTR);
|
||||
Assert.assertEquals(securityContext.getAuthentication().getName(), username);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void userAuthenticateFails() throws Exception {
|
||||
final String username = "user";
|
||||
mockMvc.perform(post("/j_spring_security_check").param("j_username", username).param("j_password", "invalid"))
|
||||
.andExpect(redirectedUrl("/spring_security_login?login_error"))
|
||||
.andExpect(new ResultMatcher() {
|
||||
public void match(MvcResult mvcResult) throws Exception {
|
||||
HttpSession session = mvcResult.getRequest().getSession();
|
||||
SecurityContext securityContext = (SecurityContext) session.getAttribute(SEC_CONTEXT_ATTR);
|
||||
Assert.assertNull(securityContext);
|
||||
}
|
||||
});
|
||||
mockMvc.perform(post("/j_spring_security_check").//
|
||||
param("j_username", username).//
|
||||
param("j_password", "invalid")).//
|
||||
andExpect(redirectedUrl("/spring_security_login?login_error")).//
|
||||
andExpect(new ResultMatcher() {
|
||||
|
||||
public void match(MvcResult mvcResult) throws Exception {
|
||||
HttpSession session = mvcResult.getRequest().getSession();
|
||||
SecurityContext securityContext = (SecurityContext) session.getAttribute(SEC_CONTEXT_ATTR);
|
||||
Assert.assertNull(securityContext);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.test.web.mock.servlet.samples.context;
|
||||
|
||||
class WebContextLoader extends GenericWebContextLoader {
|
||||
|
||||
public WebContextLoader() {
|
||||
super("src/test/resources/META-INF/web-resources", false);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,9 +16,8 @@
|
||||
|
||||
package org.springframework.test.web.mock.servlet.samples.context;
|
||||
|
||||
import static org.springframework.test.web.mock.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.mock.servlet.result.MockMvcResultMatchers.forwardedUrl;
|
||||
import static org.springframework.test.web.mock.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.test.web.mock.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.mock.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -26,23 +25,20 @@ import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.mock.servlet.MockMvc;
|
||||
import org.springframework.test.web.mock.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.context.ContextLoader;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
/**
|
||||
* Tests with XML configuration.
|
||||
*
|
||||
* The TestContext framework doesn't support WebApplicationContext yet:
|
||||
* https://jira.springsource.org/browse/SPR-5243
|
||||
*
|
||||
* A custom {@link ContextLoader} is used to load the WebApplicationContext.
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(
|
||||
loader=WebContextLoader.class,
|
||||
locations={"/org/springframework/test/web/mock/servlet/samples/servlet-context.xml"})
|
||||
@WebAppConfiguration("src/test/resources/META-INF/web-resources")
|
||||
@ContextConfiguration("../servlet-context.xml")
|
||||
public class XmlTestContextTests {
|
||||
|
||||
@Autowired
|
||||
@@ -50,6 +46,7 @@ public class XmlTestContextTests {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
||||
@@ -57,10 +54,9 @@ public class XmlTestContextTests {
|
||||
|
||||
@Test
|
||||
public void tilesDefinitions() throws Exception {
|
||||
this.mockMvc.perform(get("/"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(forwardedUrl("/WEB-INF/layouts/standardLayout.jsp"));
|
||||
this.mockMvc.perform(get("/"))//
|
||||
.andExpect(status().isOk())//
|
||||
.andExpect(forwardedUrl("/WEB-INF/layouts/standardLayout.jsp"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user