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:
parent
3552173b81
commit
a73280ccc8
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
<configs>
|
||||
<config>src/test/java/org/springframework/test/context/junit4/profile/xml/DefaultProfileXmlConfigTests-context.xml</config>
|
||||
<config>src/test/java/org/springframework/test/context/junit4/aci/xml/MultipleInitializersXmlConfigTests-context.xml</config>
|
||||
<config>src/test/resources/org/springframework/test/context/web/BasicXmlWacTests-config.xml</config>
|
||||
</configs>
|
||||
<configSets>
|
||||
</configSets>
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@
|
|||
|
||||
package org.springframework.test.context;
|
||||
|
||||
import static org.springframework.beans.BeanUtils.instantiateClass;
|
||||
import static org.springframework.core.annotation.AnnotationUtils.findAnnotationDeclaringClass;
|
||||
import static org.springframework.beans.BeanUtils.*;
|
||||
import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
|
@ -30,6 +30,8 @@ import org.apache.commons.logging.Log;
|
|||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.context.web.WebMergedContextConfiguration;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
|
@ -54,6 +56,7 @@ abstract class ContextLoaderUtils {
|
|||
private static final Log logger = LogFactory.getLog(ContextLoaderUtils.class);
|
||||
|
||||
private static final String DEFAULT_CONTEXT_LOADER_CLASS_NAME = "org.springframework.test.context.support.DelegatingSmartContextLoader";
|
||||
private static final String DEFAULT_WEB_CONTEXT_LOADER_CLASS_NAME = "org.springframework.test.context.support.WebDelegatingSmartContextLoader";
|
||||
|
||||
|
||||
private ContextLoaderUtils() {
|
||||
|
|
@ -83,7 +86,8 @@ abstract class ContextLoaderUtils {
|
|||
Assert.notNull(testClass, "Test class must not be null");
|
||||
|
||||
if (!StringUtils.hasText(defaultContextLoaderClassName)) {
|
||||
defaultContextLoaderClassName = DEFAULT_CONTEXT_LOADER_CLASS_NAME;
|
||||
defaultContextLoaderClassName = testClass.isAnnotationPresent(WebAppConfiguration.class) ? DEFAULT_WEB_CONTEXT_LOADER_CLASS_NAME
|
||||
: DEFAULT_CONTEXT_LOADER_CLASS_NAME;
|
||||
}
|
||||
|
||||
Class<? extends ContextLoader> contextLoaderClass = resolveContextLoaderClass(testClass,
|
||||
|
|
@ -394,6 +398,14 @@ abstract class ContextLoaderUtils {
|
|||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses = resolveInitializerClasses(configAttributesList);
|
||||
String[] activeProfiles = resolveActiveProfiles(testClass);
|
||||
|
||||
if (testClass.isAnnotationPresent(WebAppConfiguration.class)) {
|
||||
WebAppConfiguration webAppConfig = testClass.getAnnotation(WebAppConfiguration.class);
|
||||
String resourceBasePath = webAppConfig.value();
|
||||
return new WebMergedContextConfiguration(testClass, locations, classes, initializerClasses, activeProfiles,
|
||||
resourceBasePath, contextLoader);
|
||||
}
|
||||
|
||||
// else
|
||||
return new MergedContextConfiguration(testClass, locations, classes, initializerClasses, activeProfiles,
|
||||
contextLoader);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ public class MergedContextConfiguration implements Serializable {
|
|||
* {@link ContextLoader} based solely on the fully qualified name of the
|
||||
* loader or "null" if the supplied loaded is <code>null</code>.
|
||||
*/
|
||||
private static String nullSafeToString(ContextLoader contextLoader) {
|
||||
protected static String nullSafeToString(ContextLoader contextLoader) {
|
||||
return contextLoader == null ? "null" : contextLoader.getClass().getName();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ public class TestContext extends AttributeAccessorSupport {
|
|||
*/
|
||||
private ApplicationContext loadApplicationContext() throws Exception {
|
||||
ContextLoader contextLoader = mergedContextConfiguration.getContextLoader();
|
||||
Assert.notNull(contextLoader, "Can not load an ApplicationContext with a NULL 'contextLoader'. "
|
||||
Assert.notNull(contextLoader, "Cannot load an ApplicationContext with a NULL 'contextLoader'. "
|
||||
+ "Consider annotating your test class with @ContextConfiguration.");
|
||||
|
||||
ApplicationContext applicationContext;
|
||||
|
|
@ -125,7 +125,7 @@ public class TestContext extends AttributeAccessorSupport {
|
|||
}
|
||||
else {
|
||||
String[] locations = mergedContextConfiguration.getLocations();
|
||||
Assert.notNull(locations, "Can not load an ApplicationContext with a NULL 'locations' array. "
|
||||
Assert.notNull(locations, "Cannot load an ApplicationContext with a NULL 'locations' array. "
|
||||
+ "Consider annotating your test class with @ContextConfiguration.");
|
||||
applicationContext = contextLoader.loadContext(locations);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ import org.springframework.util.ObjectUtils;
|
|||
public class TestContextManager {
|
||||
|
||||
private static final String[] DEFAULT_TEST_EXECUTION_LISTENER_CLASS_NAMES = new String[] {
|
||||
"org.springframework.test.context.web.WebTestExecutionListener",
|
||||
"org.springframework.test.context.support.DependencyInjectionTestExecutionListener",
|
||||
"org.springframework.test.context.support.DirtiesContextTestExecutionListener",
|
||||
"org.springframework.test.context.transaction.TransactionalTestExecutionListener" };
|
||||
|
|
@ -126,7 +127,6 @@ public class TestContextManager {
|
|||
return this.testContext;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register the supplied {@link TestExecutionListener TestExecutionListeners}
|
||||
* by appending them to the set of listeners used by this <code>TestContextManager</code>.
|
||||
|
|
@ -155,8 +155,8 @@ public class TestContextManager {
|
|||
* registered for this <code>TestContextManager</code> in reverse order.
|
||||
*/
|
||||
private List<TestExecutionListener> getReversedTestExecutionListeners() {
|
||||
List<TestExecutionListener> listenersReversed =
|
||||
new ArrayList<TestExecutionListener>(getTestExecutionListeners());
|
||||
List<TestExecutionListener> listenersReversed = new ArrayList<TestExecutionListener>(
|
||||
getTestExecutionListeners());
|
||||
Collections.reverse(listenersReversed);
|
||||
return listenersReversed;
|
||||
}
|
||||
|
|
@ -186,8 +186,7 @@ public class TestContextManager {
|
|||
}
|
||||
classesList.addAll(getDefaultTestExecutionListenerClasses());
|
||||
defaultListeners = true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Traverse the class hierarchy...
|
||||
while (declaringClass != null) {
|
||||
TestExecutionListeners testExecutionListeners = declaringClass.getAnnotation(annotationType);
|
||||
|
|
@ -200,22 +199,21 @@ public class TestContextManager {
|
|||
Class<? extends TestExecutionListener>[] listenerClasses = testExecutionListeners.listeners();
|
||||
if (!ObjectUtils.isEmpty(valueListenerClasses) && !ObjectUtils.isEmpty(listenerClasses)) {
|
||||
String msg = String.format(
|
||||
"Test class [%s] has been configured with @TestExecutionListeners' 'value' [%s] " +
|
||||
"and 'listeners' [%s] attributes. Use one or the other, but not both.",
|
||||
"Test class [%s] has been configured with @TestExecutionListeners' 'value' [%s] "
|
||||
+ "and 'listeners' [%s] attributes. Use one or the other, but not both.",
|
||||
declaringClass, ObjectUtils.nullSafeToString(valueListenerClasses),
|
||||
ObjectUtils.nullSafeToString(listenerClasses));
|
||||
logger.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
else if (!ObjectUtils.isEmpty(valueListenerClasses)) {
|
||||
} else if (!ObjectUtils.isEmpty(valueListenerClasses)) {
|
||||
listenerClasses = valueListenerClasses;
|
||||
}
|
||||
|
||||
if (listenerClasses != null) {
|
||||
classesList.addAll(0, Arrays.<Class<? extends TestExecutionListener>> asList(listenerClasses));
|
||||
}
|
||||
declaringClass = (testExecutionListeners.inheritListeners() ?
|
||||
AnnotationUtils.findAnnotationDeclaringClass(annotationType, declaringClass.getSuperclass()) : null);
|
||||
declaringClass = (testExecutionListeners.inheritListeners() ? AnnotationUtils.findAnnotationDeclaringClass(
|
||||
annotationType, declaringClass.getSuperclass()) : null);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -223,16 +221,14 @@ public class TestContextManager {
|
|||
for (Class<? extends TestExecutionListener> listenerClass : classesList) {
|
||||
try {
|
||||
listeners.add(BeanUtils.instantiateClass(listenerClass));
|
||||
}
|
||||
catch (NoClassDefFoundError err) {
|
||||
} catch (NoClassDefFoundError err) {
|
||||
if (defaultListeners) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not instantiate default TestExecutionListener class ["
|
||||
+ listenerClass.getName()
|
||||
+ "]. Specify custom listener classes or make the default listener classes available.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
|
@ -245,24 +241,21 @@ public class TestContextManager {
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Set<Class<? extends TestExecutionListener>> getDefaultTestExecutionListenerClasses() {
|
||||
Set<Class<? extends TestExecutionListener>> defaultListenerClasses =
|
||||
new LinkedHashSet<Class<? extends TestExecutionListener>>();
|
||||
Set<Class<? extends TestExecutionListener>> defaultListenerClasses = new LinkedHashSet<Class<? extends TestExecutionListener>>();
|
||||
for (String className : DEFAULT_TEST_EXECUTION_LISTENER_CLASS_NAMES) {
|
||||
try {
|
||||
defaultListenerClasses.add(
|
||||
(Class<? extends TestExecutionListener>) getClass().getClassLoader().loadClass(className));
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
defaultListenerClasses.add((Class<? extends TestExecutionListener>) getClass().getClassLoader().loadClass(
|
||||
className));
|
||||
} catch (Throwable t) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Could not load default TestExecutionListener class [" + className
|
||||
+ "]. Specify custom listener classes or make the default listener classes available.");
|
||||
+ "]. Specify custom listener classes or make the default listener classes available.", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultListenerClasses;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hook for pre-processing a test class <em>before</em> execution of any
|
||||
* tests within the class. Should be called prior to any framework-specific
|
||||
|
|
@ -286,8 +279,7 @@ public class TestContextManager {
|
|||
for (TestExecutionListener testExecutionListener : getTestExecutionListeners()) {
|
||||
try {
|
||||
testExecutionListener.beforeTestClass(getTestContext());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
} catch (Exception ex) {
|
||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener
|
||||
+ "] to process 'before class' callback for test class [" + testClass + "]", ex);
|
||||
throw ex;
|
||||
|
|
@ -319,8 +311,7 @@ public class TestContextManager {
|
|||
for (TestExecutionListener testExecutionListener : getTestExecutionListeners()) {
|
||||
try {
|
||||
testExecutionListener.prepareTestInstance(getTestContext());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
} catch (Exception ex) {
|
||||
logger.error("Caught exception while allowing TestExecutionListener [" + testExecutionListener
|
||||
+ "] to prepare test instance [" + testInstance + "]", ex);
|
||||
throw ex;
|
||||
|
|
@ -356,8 +347,7 @@ public class TestContextManager {
|
|||
for (TestExecutionListener testExecutionListener : getTestExecutionListeners()) {
|
||||
try {
|
||||
testExecutionListener.beforeTestMethod(getTestContext());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
} catch (Exception ex) {
|
||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener
|
||||
+ "] to process 'before' execution of test method [" + testMethod + "] for test instance ["
|
||||
+ testInstance + "]", ex);
|
||||
|
|
@ -404,8 +394,7 @@ public class TestContextManager {
|
|||
for (TestExecutionListener testExecutionListener : getReversedTestExecutionListeners()) {
|
||||
try {
|
||||
testExecutionListener.afterTestMethod(getTestContext());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
} catch (Exception ex) {
|
||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener
|
||||
+ "] to process 'after' execution for test: method [" + testMethod + "], instance ["
|
||||
+ testInstance + "], exception [" + exception + "]", ex);
|
||||
|
|
@ -446,8 +435,7 @@ public class TestContextManager {
|
|||
for (TestExecutionListener testExecutionListener : getReversedTestExecutionListeners()) {
|
||||
try {
|
||||
testExecutionListener.afterTestClass(getTestContext());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
} catch (Exception ex) {
|
||||
logger.warn("Caught exception while allowing TestExecutionListener [" + testExecutionListener
|
||||
+ "] to process 'after class' callback for test class [" + testClass + "]", ex);
|
||||
if (afterTestClassException == null) {
|
||||
|
|
|
|||
|
|
@ -16,13 +16,24 @@
|
|||
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.support.ResourcePatternUtils;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.SmartContextLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
|
@ -81,6 +92,64 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
|
|||
configAttributes.setLocations(processedLocations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the {@link ConfigurableApplicationContext} created by this
|
||||
* {@code SmartContextLoader} <i>before</i> bean definitions are read.
|
||||
*
|
||||
* <p>The default implementation:
|
||||
* <ul>
|
||||
* <li>Sets the <em>active bean definition profiles</em> from the supplied
|
||||
* <code>MergedContextConfiguration</code> in the
|
||||
* {@link org.springframework.core.env.Environment Environment} of the context.</li>
|
||||
* <li>Determines what (if any) context initializer classes have been supplied
|
||||
* via the {@code MergedContextConfiguration} and
|
||||
* {@linkplain ApplicationContextInitializer#initialize invokes each} with the
|
||||
* given application context.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Any {@code ApplicationContextInitializers} implementing
|
||||
* {@link org.springframework.core.Ordered Ordered} or marked with {@link
|
||||
* org.springframework.core.annotation.Order @Order} will be sorted appropriately.
|
||||
*
|
||||
* @param context the newly created application context
|
||||
* @param mergedConfig the merged context configuration
|
||||
* @see ApplicationContextInitializer#initialize(ConfigurableApplicationContext)
|
||||
* @see #loadContext(MergedContextConfiguration)
|
||||
* @see ConfigurableApplicationContext#setId
|
||||
* @since 3.2
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void prepareContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
|
||||
|
||||
context.getEnvironment().setActiveProfiles(mergedConfig.getActiveProfiles());
|
||||
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses = mergedConfig.getContextInitializerClasses();
|
||||
|
||||
if (initializerClasses.size() == 0) {
|
||||
// no ApplicationContextInitializers have been declared -> nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
final List<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances = new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
|
||||
final Class<?> contextClass = context.getClass();
|
||||
|
||||
for (Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>> initializerClass : initializerClasses) {
|
||||
Class<?> initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass,
|
||||
ApplicationContextInitializer.class);
|
||||
Assert.isAssignable(initializerContextClass, contextClass, String.format(
|
||||
"Could not add context initializer [%s] since its generic parameter [%s] "
|
||||
+ "is not assignable from the type of application context used by this "
|
||||
+ "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
|
||||
contextClass.getName()));
|
||||
initializerInstances.add((ApplicationContextInitializer<ConfigurableApplicationContext>) BeanUtils.instantiateClass(initializerClass));
|
||||
}
|
||||
|
||||
Collections.sort(initializerInstances, new AnnotationAwareOrderComparator());
|
||||
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {
|
||||
initializer.initialize(context);
|
||||
}
|
||||
}
|
||||
|
||||
// --- ContextLoader -------------------------------------------------------
|
||||
|
||||
/**
|
||||
|
|
@ -185,12 +254,10 @@ public abstract class AbstractContextLoader implements SmartContextLoader {
|
|||
String path = locations[i];
|
||||
if (path.startsWith(SLASH)) {
|
||||
modifiedLocations[i] = ResourceUtils.CLASSPATH_URL_PREFIX + path;
|
||||
}
|
||||
else if (!ResourcePatternUtils.isUrl(path)) {
|
||||
} else if (!ResourcePatternUtils.isUrl(path)) {
|
||||
modifiedLocations[i] = ResourceUtils.CLASSPATH_URL_PREFIX + SLASH
|
||||
+ StringUtils.cleanPath(ClassUtils.classPackageAsResourcePath(clazz) + SLASH + path);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
modifiedLocations[i] = StringUtils.cleanPath(path);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
* 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.context.support;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.SmartContextLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* {@code AbstractDelegatingSmartContextLoader} serves as an abstract base class
|
||||
* for implementations of the {@link SmartContextLoader} SPI that delegate to a
|
||||
* set of <em>candidate</em> SmartContextLoaders (i.e., one that supports XML
|
||||
* configuration files and one that supports annotated classes) to determine which
|
||||
* context loader is appropriate for a given test class's configuration. Each
|
||||
* candidate is given a chance to {@link #processContextConfiguration process} the
|
||||
* {@link ContextConfigurationAttributes} for each class in the test class hierarchy
|
||||
* that is annotated with {@link ContextConfiguration @ContextConfiguration}, and
|
||||
* the candidate that supports the merged, processed configuration will be used to
|
||||
* actually {@link #loadContext load} the context.
|
||||
*
|
||||
* <p>Placing an empty {@code @ContextConfiguration} annotation on a test class signals
|
||||
* that default resource locations (i.e., XML configuration files) or default
|
||||
* {@link org.springframework.context.annotation.Configuration configuration classes}
|
||||
* should be detected. Furthermore, if a specific {@link ContextLoader} or
|
||||
* {@link SmartContextLoader} is not explicitly declared via
|
||||
* {@code @ContextConfiguration}, a concrete subclass of
|
||||
* {@code AbstractDelegatingSmartContextLoader} will be used as the default loader,
|
||||
* thus providing automatic support for either XML configuration files or annotated
|
||||
* classes, but not both simultaneously.
|
||||
*
|
||||
* <p>As of Spring 3.2, a test class may optionally declare neither XML configuration
|
||||
* files nor annotated classes and instead declare only {@linkplain
|
||||
* ContextConfiguration#initializers application context initializers}. In such
|
||||
* cases, an attempt will still be made to detect defaults, but their absence will
|
||||
* not result an an exception.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.2
|
||||
* @see SmartContextLoader
|
||||
*/
|
||||
abstract class AbstractDelegatingSmartContextLoader implements SmartContextLoader {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AbstractDelegatingSmartContextLoader.class);
|
||||
|
||||
|
||||
/**
|
||||
* Get the delegate {@code SmartContextLoader} that supports XML configuration files.
|
||||
*/
|
||||
protected abstract SmartContextLoader getXmlLoader();
|
||||
|
||||
/**
|
||||
* Get the delegate {@code SmartContextLoader} that supports annotated classes.
|
||||
*/
|
||||
protected abstract SmartContextLoader getAnnotationConfigLoader();
|
||||
|
||||
// --- SmartContextLoader --------------------------------------------------
|
||||
|
||||
private static String name(SmartContextLoader loader) {
|
||||
return loader.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
private static void delegateProcessing(SmartContextLoader loader, ContextConfigurationAttributes configAttributes) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Delegating to %s to process context configuration %s.", name(loader),
|
||||
configAttributes));
|
||||
}
|
||||
loader.processContextConfiguration(configAttributes);
|
||||
}
|
||||
|
||||
private static ApplicationContext delegateLoading(SmartContextLoader loader, MergedContextConfiguration mergedConfig)
|
||||
throws Exception {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Delegating to %s to load context from %s.", name(loader), mergedConfig));
|
||||
}
|
||||
return loader.loadContext(mergedConfig);
|
||||
}
|
||||
|
||||
private boolean supports(SmartContextLoader loader, MergedContextConfiguration mergedConfig) {
|
||||
if (loader == getAnnotationConfigLoader()) {
|
||||
return ObjectUtils.isEmpty(mergedConfig.getLocations()) && !ObjectUtils.isEmpty(mergedConfig.getClasses());
|
||||
} else {
|
||||
return !ObjectUtils.isEmpty(mergedConfig.getLocations()) && ObjectUtils.isEmpty(mergedConfig.getClasses());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to candidate {@code SmartContextLoaders} to process the supplied
|
||||
* {@link ContextConfigurationAttributes}.
|
||||
*
|
||||
* <p>Delegation is based on explicit knowledge of the implementations of the
|
||||
* default loaders for {@link #getXmlLoader() XML configuration files} and
|
||||
* {@link #getAnnotationConfigLoader() annotated classes}. Specifically, the
|
||||
* delegation algorithm is as follows:
|
||||
*
|
||||
* <ul>
|
||||
* <li>If the resource locations or annotated classes in the supplied
|
||||
* {@code ContextConfigurationAttributes} are not empty, the appropriate
|
||||
* candidate loader will be allowed to process the configuration <em>as is</em>,
|
||||
* without any checks for detection of defaults.</li>
|
||||
* <li>Otherwise, the XML-based loader will be allowed to process
|
||||
* the configuration in order to detect default resource locations. If
|
||||
* the XML-based loader detects default resource locations,
|
||||
* an {@code info} message will be logged.</li>
|
||||
* <li>Subsequently, the annotation-based loader will be allowed to
|
||||
* process the configuration in order to detect default configuration classes.
|
||||
* If the annotation-based loader detects default configuration
|
||||
* classes, an {@code info} message will be logged.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param configAttributes the context configuration attributes to process
|
||||
* @throws IllegalArgumentException if the supplied configuration attributes are
|
||||
* <code>null</code>, or if the supplied configuration attributes include both
|
||||
* resource locations and annotated classes
|
||||
* @throws IllegalStateException if the XML-based loader detects default
|
||||
* configuration classes; if the annotation-based loader detects default
|
||||
* resource locations; if neither candidate loader detects defaults for the supplied
|
||||
* context configuration; or if both candidate loaders detect defaults for the
|
||||
* supplied context configuration
|
||||
*/
|
||||
public void processContextConfiguration(final ContextConfigurationAttributes configAttributes) {
|
||||
|
||||
Assert.notNull(configAttributes, "configAttributes must not be null");
|
||||
Assert.isTrue(!(configAttributes.hasLocations() && configAttributes.hasClasses()), String.format(
|
||||
"Cannot process locations AND classes for context "
|
||||
+ "configuration %s; configure one or the other, but not both.", configAttributes));
|
||||
|
||||
// If the original locations or classes were not empty, there's no
|
||||
// need to bother with default detection checks; just let the
|
||||
// appropriate loader process the configuration.
|
||||
if (configAttributes.hasLocations()) {
|
||||
delegateProcessing(getXmlLoader(), configAttributes);
|
||||
} else if (configAttributes.hasClasses()) {
|
||||
delegateProcessing(getAnnotationConfigLoader(), configAttributes);
|
||||
} else {
|
||||
// Else attempt to detect defaults...
|
||||
|
||||
// Let the XML loader process the configuration.
|
||||
delegateProcessing(getXmlLoader(), configAttributes);
|
||||
boolean xmlLoaderDetectedDefaults = configAttributes.hasLocations();
|
||||
|
||||
if (xmlLoaderDetectedDefaults) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format("%s detected default locations for context configuration %s.",
|
||||
name(getXmlLoader()), configAttributes));
|
||||
}
|
||||
}
|
||||
|
||||
if (configAttributes.hasClasses()) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"%s should NOT have detected default configuration classes for context configuration %s.",
|
||||
name(getXmlLoader()), configAttributes));
|
||||
}
|
||||
|
||||
// Now let the annotation config loader process the configuration.
|
||||
delegateProcessing(getAnnotationConfigLoader(), configAttributes);
|
||||
|
||||
if (configAttributes.hasClasses()) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format(
|
||||
"%s detected default configuration classes for context configuration %s.",
|
||||
name(getAnnotationConfigLoader()), configAttributes));
|
||||
}
|
||||
}
|
||||
|
||||
if (!xmlLoaderDetectedDefaults && configAttributes.hasLocations()) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"%s should NOT have detected default locations for context configuration %s.",
|
||||
name(getAnnotationConfigLoader()), configAttributes));
|
||||
}
|
||||
|
||||
// If neither loader detected defaults and no initializers were declared,
|
||||
// throw an exception.
|
||||
if (!configAttributes.hasResources() && ObjectUtils.isEmpty(configAttributes.getInitializers())) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Neither %s nor %s was able to detect defaults, and no ApplicationContextInitializers "
|
||||
+ "were declared for context configuration %s", name(getXmlLoader()),
|
||||
name(getAnnotationConfigLoader()), configAttributes));
|
||||
}
|
||||
|
||||
if (configAttributes.hasLocations() && configAttributes.hasClasses()) {
|
||||
String message = String.format(
|
||||
"Configuration error: both default locations AND default configuration classes "
|
||||
+ "were detected for context configuration %s; configure one or the other, but not both.",
|
||||
configAttributes);
|
||||
logger.error(message);
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to an appropriate candidate {@code SmartContextLoader} to load
|
||||
* an {@link ApplicationContext}.
|
||||
*
|
||||
* <p>Delegation is based on explicit knowledge of the implementations of the
|
||||
* default loaders for {@link #getXmlLoader() XML configuration files} and
|
||||
* {@link #getAnnotationConfigLoader() annotated classes}. Specifically, the
|
||||
* delegation algorithm is as follows:
|
||||
*
|
||||
* <ul>
|
||||
* <li>If the resource locations in the supplied {@code MergedContextConfiguration}
|
||||
* are not empty and the annotated classes are empty,
|
||||
* the XML-based loader will load the {@code ApplicationContext}.</li>
|
||||
* <li>If the annotated classes in the supplied {@code MergedContextConfiguration}
|
||||
* are not empty and the resource locations are empty,
|
||||
* the annotation-based loader will load the {@code ApplicationContext}.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param mergedConfig the merged context configuration to use to load the application context
|
||||
* @throws IllegalArgumentException if the supplied merged configuration is <code>null</code>
|
||||
* @throws IllegalStateException if neither candidate loader is capable of loading an
|
||||
* {@code ApplicationContext} from the supplied merged context configuration
|
||||
*/
|
||||
public ApplicationContext loadContext(MergedContextConfiguration mergedConfig) throws Exception {
|
||||
Assert.notNull(mergedConfig, "mergedConfig must not be null");
|
||||
|
||||
List<SmartContextLoader> candidates = Arrays.asList(getXmlLoader(), getAnnotationConfigLoader());
|
||||
|
||||
for (SmartContextLoader loader : candidates) {
|
||||
// Determine if each loader can load a context from the mergedConfig. If it
|
||||
// can, let it; otherwise, keep iterating.
|
||||
if (supports(loader, mergedConfig)) {
|
||||
return delegateLoading(loader, mergedConfig);
|
||||
}
|
||||
}
|
||||
|
||||
// If neither of the candidates supports the mergedConfig based on resources but
|
||||
// ACIs were declared, then delegate to the annotation config loader.
|
||||
if (!mergedConfig.getContextInitializerClasses().isEmpty()) {
|
||||
return delegateLoading(getAnnotationConfigLoader(), mergedConfig);
|
||||
}
|
||||
|
||||
throw new IllegalStateException(String.format(
|
||||
"Neither %s nor %s was able to load an ApplicationContext from %s.", name(getXmlLoader()),
|
||||
name(getAnnotationConfigLoader()), mergedConfig));
|
||||
}
|
||||
|
||||
// --- ContextLoader -------------------------------------------------------
|
||||
|
||||
/**
|
||||
* {@code AbstractDelegatingSmartContextLoader} does not support the
|
||||
* {@link ContextLoader#processLocations(Class, String...)} method. Call
|
||||
* {@link #processContextConfiguration(ContextConfigurationAttributes)} instead.
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
public final String[] processLocations(Class<?> clazz, String... locations) {
|
||||
throw new UnsupportedOperationException("DelegatingSmartContextLoaders do not support the ContextLoader SPI. "
|
||||
+ "Call processContextConfiguration(ContextConfigurationAttributes) instead.");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code AbstractDelegatingSmartContextLoader} does not support the
|
||||
* {@link ContextLoader#loadContext(String...) } method. Call
|
||||
* {@link #loadContext(MergedContextConfiguration)} instead.
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
public final ApplicationContext loadContext(String... locations) throws Exception {
|
||||
throw new UnsupportedOperationException("DelegatingSmartContextLoaders do not support the ContextLoader SPI. "
|
||||
+ "Call loadContext(MergedContextConfiguration) instead.");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,25 +16,14 @@
|
|||
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReader;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
|
|
@ -77,7 +66,10 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
|
|||
*
|
||||
* <ul>
|
||||
* <li>Creates a {@link GenericApplicationContext} instance.</li>
|
||||
* <li>Calls {@link #prepareContext(GenericApplicationContext, MergedContextConfiguration)}
|
||||
* <li>Calls {@link #prepareContext(GenericApplicationContext)} for backwards
|
||||
* compatibility with the {@link org.springframework.test.context.ContextLoader
|
||||
* ContextLoader} SPI.</li>
|
||||
* <li>Calls {@link #prepareContext(ConfigurableApplicationContext, MergedContextConfiguration)}
|
||||
* to allow for customizing the context before bean definitions are loaded.</li>
|
||||
* <li>Calls {@link #customizeBeanFactory(DefaultListableBeanFactory)} to allow for customizing the
|
||||
* context's <code>DefaultListableBeanFactory</code>.</li>
|
||||
|
|
@ -105,6 +97,7 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
|
|||
}
|
||||
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
prepareContext(context);
|
||||
prepareContext(context, mergedConfig);
|
||||
customizeBeanFactory(context.getDefaultListableBeanFactory());
|
||||
loadBeanDefinitions(context, mergedConfig);
|
||||
|
|
@ -183,72 +176,6 @@ public abstract class AbstractGenericContextLoader extends AbstractContextLoader
|
|||
protected void prepareContext(GenericApplicationContext context) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the {@link GenericApplicationContext} created by this
|
||||
* {@code SmartContextLoader} <i>before</i> bean definitions are read.
|
||||
*
|
||||
* <p>The default implementation:
|
||||
* <ul>
|
||||
* <li>Calls {@link #prepareContext(GenericApplicationContext)} for backwards
|
||||
* compatibility with the {@link org.springframework.test.context.ContextLoader
|
||||
* ContextLoader} SPI.</li>
|
||||
* <li>Sets the <em>active bean definition profiles</em> from the supplied
|
||||
* <code>MergedContextConfiguration</code> in the
|
||||
* {@link org.springframework.core.env.Environment Environment} of the context.</li>
|
||||
* <li>Determines what (if any) context initializer classes have been supplied
|
||||
* via the {@code MergedContextConfiguration} and
|
||||
* {@linkplain ApplicationContextInitializer#initialize invokes each} with the
|
||||
* given application context.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Any {@code ApplicationContextInitializers} implementing
|
||||
* {@link org.springframework.core.Ordered Ordered} or marked with {@link
|
||||
* org.springframework.core.annotation.Order @Order} will be sorted appropriately.
|
||||
*
|
||||
* @param applicationContext the newly created application context
|
||||
* @param mergedConfig the merged context configuration
|
||||
* @see ApplicationContextInitializer#initialize(GenericApplicationContext)
|
||||
* @see #loadContext(MergedContextConfiguration)
|
||||
* @see GenericApplicationContext#setAllowBeanDefinitionOverriding
|
||||
* @see GenericApplicationContext#setResourceLoader
|
||||
* @see GenericApplicationContext#setId
|
||||
* @since 3.2
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void prepareContext(GenericApplicationContext applicationContext,
|
||||
MergedContextConfiguration mergedConfig) {
|
||||
|
||||
prepareContext(applicationContext);
|
||||
|
||||
applicationContext.getEnvironment().setActiveProfiles(mergedConfig.getActiveProfiles());
|
||||
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> initializerClasses = mergedConfig.getContextInitializerClasses();
|
||||
|
||||
if (initializerClasses.size() == 0) {
|
||||
// no ApplicationContextInitializers have been declared -> nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
final List<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances = new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
|
||||
final Class<?> contextClass = applicationContext.getClass();
|
||||
|
||||
for (Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>> initializerClass : initializerClasses) {
|
||||
Class<?> initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass,
|
||||
ApplicationContextInitializer.class);
|
||||
Assert.isAssignable(initializerContextClass, contextClass, String.format(
|
||||
"Could not add context initializer [%s] since its generic parameter [%s] "
|
||||
+ "is not assignable from the type of application context used by this "
|
||||
+ "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
|
||||
contextClass.getName()));
|
||||
initializerInstances.add((ApplicationContextInitializer<ConfigurableApplicationContext>) BeanUtils.instantiateClass(initializerClass));
|
||||
}
|
||||
|
||||
Collections.sort(initializerInstances, new AnnotationAwareOrderComparator());
|
||||
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {
|
||||
initializer.initialize(applicationContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize the internal bean factory of the ApplicationContext created by
|
||||
* this <code>ContextLoader</code>.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* 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.context.support;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
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.MockServletContext;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.web.WebMergedContextConfiguration;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document AbstractGenericWebContextLoader.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.2
|
||||
*/
|
||||
public abstract class AbstractGenericWebContextLoader extends AbstractContextLoader {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AbstractGenericWebContextLoader.class);
|
||||
|
||||
|
||||
// --- SmartContextLoader -----------------------------------------------
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document overridden loadContext(MergedContextConfiguration).
|
||||
*
|
||||
* @see org.springframework.test.context.SmartContextLoader#loadContext(org.springframework.test.context.MergedContextConfiguration)
|
||||
*/
|
||||
public final ConfigurableApplicationContext loadContext(MergedContextConfiguration mergedConfig) throws Exception {
|
||||
|
||||
if (!(mergedConfig instanceof WebMergedContextConfiguration)) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Cannot load WebApplicationContext from non-web merged context configuration %s. "
|
||||
+ "Consider annotating your test class with @WebAppConfiguration.", mergedConfig));
|
||||
}
|
||||
WebMergedContextConfiguration webMergedConfig = (WebMergedContextConfiguration) mergedConfig;
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Loading WebApplicationContext for merged context configuration %s.",
|
||||
webMergedConfig));
|
||||
}
|
||||
|
||||
GenericWebApplicationContext context = new GenericWebApplicationContext();
|
||||
configureWebResources(context, webMergedConfig);
|
||||
prepareContext(context, webMergedConfig);
|
||||
customizeBeanFactory(context.getDefaultListableBeanFactory(), webMergedConfig);
|
||||
loadBeanDefinitions(context, webMergedConfig);
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(context);
|
||||
customizeContext(context, webMergedConfig);
|
||||
context.refresh();
|
||||
context.registerShutdownHook();
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document configureWebResources().
|
||||
*/
|
||||
protected void configureWebResources(GenericWebApplicationContext context,
|
||||
WebMergedContextConfiguration webMergedConfig) {
|
||||
|
||||
String resourceBasePath = webMergedConfig.getResourceBasePath();
|
||||
ResourceLoader resourceLoader = resourceBasePath.startsWith(ResourceLoader.CLASSPATH_URL_PREFIX) ? new DefaultResourceLoader()
|
||||
: new FileSystemResourceLoader();
|
||||
|
||||
ServletContext servletContext = new MockServletContext(resourceBasePath, resourceLoader);
|
||||
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
|
||||
context.setServletContext(servletContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document customizeBeanFactory().
|
||||
*/
|
||||
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory,
|
||||
WebMergedContextConfiguration webMergedConfig) {
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document loadBeanDefinitions().
|
||||
*/
|
||||
protected abstract void loadBeanDefinitions(GenericWebApplicationContext context,
|
||||
WebMergedContextConfiguration webMergedConfig);
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document customizeContext().
|
||||
*/
|
||||
protected void customizeContext(GenericWebApplicationContext context, WebMergedContextConfiguration webMergedConfig) {
|
||||
}
|
||||
|
||||
// --- ContextLoader -------------------------------------------------------
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document overridden loadContext(String...).
|
||||
*
|
||||
* @see org.springframework.test.context.ContextLoader#loadContext(java.lang.String[])
|
||||
*/
|
||||
public final ApplicationContext loadContext(String... locations) throws Exception {
|
||||
throw new UnsupportedOperationException(
|
||||
"AbstractGenericWebContextLoader does not support the loadContext(String... locations) method");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,19 +16,13 @@
|
|||
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReader;
|
||||
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
|
|
@ -89,80 +83,19 @@ public class AnnotationConfigContextLoader extends AbstractGenericContextLoader
|
|||
|
||||
// --- AnnotationConfigContextLoader ---------------------------------------
|
||||
|
||||
private boolean isStaticNonPrivateAndNonFinal(Class<?> clazz) {
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
int modifiers = clazz.getModifiers();
|
||||
return (Modifier.isStatic(modifiers) && !Modifier.isPrivate(modifiers) && !Modifier.isFinal(modifiers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the supplied {@link Class} meets the criteria for being
|
||||
* considered a <em>default configuration class</em> candidate.
|
||||
*
|
||||
* <p>Specifically, such candidates:
|
||||
*
|
||||
* <ul>
|
||||
* <li>must not be <code>null</code></li>
|
||||
* <li>must not be <code>private</code></li>
|
||||
* <li>must not be <code>final</code></li>
|
||||
* <li>must be <code>static</code></li>
|
||||
* <li>must be annotated with {@code @Configuration}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param clazz the class to check
|
||||
* @return <code>true</code> if the supplied class meets the candidate criteria
|
||||
*/
|
||||
private boolean isDefaultConfigurationClassCandidate(Class<?> clazz) {
|
||||
return clazz != null && isStaticNonPrivateAndNonFinal(clazz) && clazz.isAnnotationPresent(Configuration.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the default configuration classes for the supplied test class.
|
||||
*
|
||||
* <p>The returned class array will contain all static inner classes of
|
||||
* the supplied class that meet the requirements for {@code @Configuration}
|
||||
* class implementations as specified in the documentation for
|
||||
* {@link Configuration @Configuration}.
|
||||
* <p>The default implementation simply delegates to
|
||||
* {@link AnnotationConfigContextLoaderUtils#detectDefaultConfigurationClasses(Class)}.
|
||||
*
|
||||
* <p>The implementation of this method adheres to the contract defined in the
|
||||
* {@link org.springframework.test.context.SmartContextLoader SmartContextLoader}
|
||||
* SPI. Specifically, this method uses introspection to detect default
|
||||
* configuration classes that comply with the constraints required of
|
||||
* {@code @Configuration} class implementations. If a potential candidate
|
||||
* configuration class does not meet these requirements, this method will log a
|
||||
* warning, and the potential candidate class will be ignored.
|
||||
* @param declaringClass the test class that declared {@code @ContextConfiguration}
|
||||
* @return an array of default configuration classes, potentially empty but
|
||||
* never <code>null</code>
|
||||
* @see AnnotationConfigContextLoaderUtils
|
||||
*/
|
||||
protected Class<?>[] detectDefaultConfigurationClasses(Class<?> declaringClass) {
|
||||
Assert.notNull(declaringClass, "Declaring class must not be null");
|
||||
|
||||
List<Class<?>> configClasses = new ArrayList<Class<?>>();
|
||||
|
||||
for (Class<?> candidate : declaringClass.getDeclaredClasses()) {
|
||||
if (isDefaultConfigurationClassCandidate(candidate)) {
|
||||
configClasses.add(candidate);
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(
|
||||
"Ignoring class [%s]; it must be static, non-private, non-final, and annotated "
|
||||
+ "with @Configuration to be considered a default configuration class.",
|
||||
candidate.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (configClasses.isEmpty()) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format("Could not detect default configuration classes for test class [%s]: "
|
||||
+ "%s does not declare any static, non-private, non-final, inner classes "
|
||||
+ "annotated with @Configuration.", declaringClass.getName(), declaringClass.getSimpleName()));
|
||||
}
|
||||
}
|
||||
|
||||
return configClasses.toArray(new Class<?>[configClasses.size()]);
|
||||
return AnnotationConfigContextLoaderUtils.detectDefaultConfigurationClasses(declaringClass);
|
||||
}
|
||||
|
||||
// --- AbstractContextLoader -----------------------------------------------
|
||||
|
|
|
|||
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* 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.context.support;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document AnnotationConfigContextLoaderUtils.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.2
|
||||
*/
|
||||
abstract class AnnotationConfigContextLoaderUtils {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AnnotationConfigContextLoaderUtils.class);
|
||||
|
||||
|
||||
private AnnotationConfigContextLoaderUtils() {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
private static boolean isStaticNonPrivateAndNonFinal(Class<?> clazz) {
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
int modifiers = clazz.getModifiers();
|
||||
return (Modifier.isStatic(modifiers) && !Modifier.isPrivate(modifiers) && !Modifier.isFinal(modifiers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the supplied {@link Class} meets the criteria for being
|
||||
* considered a <em>default configuration class</em> candidate.
|
||||
*
|
||||
* <p>Specifically, such candidates:
|
||||
*
|
||||
* <ul>
|
||||
* <li>must not be <code>null</code></li>
|
||||
* <li>must not be <code>private</code></li>
|
||||
* <li>must not be <code>final</code></li>
|
||||
* <li>must be <code>static</code></li>
|
||||
* <li>must be annotated with {@code @Configuration}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param clazz the class to check
|
||||
* @return <code>true</code> if the supplied class meets the candidate criteria
|
||||
*/
|
||||
private static boolean isDefaultConfigurationClassCandidate(Class<?> clazz) {
|
||||
return clazz != null && isStaticNonPrivateAndNonFinal(clazz) && clazz.isAnnotationPresent(Configuration.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the default configuration classes for the supplied test class.
|
||||
*
|
||||
* <p>The returned class array will contain all static inner classes of
|
||||
* the supplied class that meet the requirements for {@code @Configuration}
|
||||
* class implementations as specified in the documentation for
|
||||
* {@link Configuration @Configuration}.
|
||||
*
|
||||
* <p>The implementation of this method adheres to the contract defined in the
|
||||
* {@link org.springframework.test.context.SmartContextLoader SmartContextLoader}
|
||||
* SPI. Specifically, this method uses introspection to detect default
|
||||
* configuration classes that comply with the constraints required of
|
||||
* {@code @Configuration} class implementations. If a potential candidate
|
||||
* configuration class does not meet these requirements, this method will log a
|
||||
* warning, and the potential candidate class will be ignored.
|
||||
* @param declaringClass the test class that declared {@code @ContextConfiguration}
|
||||
* @return an array of default configuration classes, potentially empty but
|
||||
* never <code>null</code>
|
||||
*/
|
||||
static Class<?>[] detectDefaultConfigurationClasses(Class<?> declaringClass) {
|
||||
Assert.notNull(declaringClass, "Declaring class must not be null");
|
||||
|
||||
List<Class<?>> configClasses = new ArrayList<Class<?>>();
|
||||
|
||||
for (Class<?> candidate : declaringClass.getDeclaredClasses()) {
|
||||
if (isDefaultConfigurationClassCandidate(candidate)) {
|
||||
configClasses.add(candidate);
|
||||
} else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(
|
||||
"Ignoring class [%s]; it must be static, non-private, non-final, and annotated "
|
||||
+ "with @Configuration to be considered a default configuration class.",
|
||||
candidate.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (configClasses.isEmpty()) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format("Could not detect default configuration classes for test class [%s]: "
|
||||
+ "%s does not declare any static, non-private, non-final, inner classes "
|
||||
+ "annotated with @Configuration.", declaringClass.getName(), declaringClass.getSimpleName()));
|
||||
}
|
||||
}
|
||||
|
||||
return configClasses.toArray(new Class<?>[configClasses.size()]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* 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.context.support;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.web.WebMergedContextConfiguration;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document AnnotationConfigWebContextLoader.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.2
|
||||
*/
|
||||
public class AnnotationConfigWebContextLoader extends AbstractGenericWebContextLoader {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AnnotationConfigWebContextLoader.class);
|
||||
|
||||
|
||||
// --- SmartContextLoader -----------------------------------------------
|
||||
|
||||
/**
|
||||
* Process <em>annotated classes</em> in the supplied {@link ContextConfigurationAttributes}.
|
||||
*
|
||||
* <p>If the <em>annotated classes</em> are <code>null</code> or empty and
|
||||
* {@link #isGenerateDefaultLocations()} returns <code>true</code>, this
|
||||
* <code>SmartContextLoader</code> will attempt to {@link
|
||||
* #detectDefaultConfigurationClasses detect default configuration classes}.
|
||||
* If defaults are detected they will be
|
||||
* {@link ContextConfigurationAttributes#setClasses(Class[]) set} in the
|
||||
* supplied configuration attributes. Otherwise, properties in the supplied
|
||||
* configuration attributes will not be modified.
|
||||
*
|
||||
* @param configAttributes the context configuration attributes to process
|
||||
* @see org.springframework.test.context.SmartContextLoader#processContextConfiguration(ContextConfigurationAttributes)
|
||||
* @see #isGenerateDefaultLocations()
|
||||
* @see #detectDefaultConfigurationClasses(Class)
|
||||
*/
|
||||
public void processContextConfiguration(ContextConfigurationAttributes configAttributes) {
|
||||
if (ObjectUtils.isEmpty(configAttributes.getClasses()) && isGenerateDefaultLocations()) {
|
||||
Class<?>[] defaultConfigClasses = detectDefaultConfigurationClasses(configAttributes.getDeclaringClass());
|
||||
configAttributes.setClasses(defaultConfigClasses);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the default configuration classes for the supplied test class.
|
||||
*
|
||||
* <p>The default implementation simply delegates to
|
||||
* {@link AnnotationConfigContextLoaderUtils#detectDefaultConfigurationClasses(Class)}.
|
||||
*
|
||||
* @param declaringClass the test class that declared {@code @ContextConfiguration}
|
||||
* @return an array of default configuration classes, potentially empty but
|
||||
* never <code>null</code>
|
||||
* @see AnnotationConfigContextLoaderUtils
|
||||
*/
|
||||
protected Class<?>[] detectDefaultConfigurationClasses(Class<?> declaringClass) {
|
||||
return AnnotationConfigContextLoaderUtils.detectDefaultConfigurationClasses(declaringClass);
|
||||
}
|
||||
|
||||
// --- AbstractContextLoader -----------------------------------------------
|
||||
|
||||
/**
|
||||
* {@code AnnotationConfigWebContextLoader} should be used as a
|
||||
* {@link org.springframework.test.context.SmartContextLoader SmartContextLoader},
|
||||
* not as a legacy {@link org.springframework.test.context.ContextLoader ContextLoader}.
|
||||
* Consequently, this method is not supported.
|
||||
*
|
||||
* @see AbstractContextLoader#modifyLocations
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
@Override
|
||||
protected String[] modifyLocations(Class<?> clazz, String... locations) {
|
||||
throw new UnsupportedOperationException(
|
||||
"AnnotationConfigWebContextLoader does not support the modifyLocations(Class, String...) method");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code AnnotationConfigWebContextLoader} should be used as a
|
||||
* {@link org.springframework.test.context.SmartContextLoader SmartContextLoader},
|
||||
* not as a legacy {@link org.springframework.test.context.ContextLoader ContextLoader}.
|
||||
* Consequently, this method is not supported.
|
||||
*
|
||||
* @see AbstractContextLoader#generateDefaultLocations
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
@Override
|
||||
protected String[] generateDefaultLocations(Class<?> clazz) {
|
||||
throw new UnsupportedOperationException(
|
||||
"AnnotationConfigWebContextLoader does not support the generateDefaultLocations(Class) method");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code AnnotationConfigWebContextLoader} should be used as a
|
||||
* {@link org.springframework.test.context.SmartContextLoader SmartContextLoader},
|
||||
* not as a legacy {@link org.springframework.test.context.ContextLoader ContextLoader}.
|
||||
* Consequently, this method is not supported.
|
||||
*
|
||||
* @see AbstractContextLoader#getResourceSuffix
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
@Override
|
||||
protected String getResourceSuffix() {
|
||||
throw new UnsupportedOperationException(
|
||||
"AnnotationConfigWebContextLoader does not support the getResourceSuffix() method");
|
||||
}
|
||||
|
||||
// --- AbstractGenericWebContextLoader -------------------------------------
|
||||
|
||||
/**
|
||||
* Register classes in the supplied {@link GenericWebApplicationContext context}
|
||||
* from the classes in the supplied {@link WebMergedContextConfiguration}.
|
||||
*
|
||||
* <p>Each class must represent an <em>annotated class</em>. An
|
||||
* {@link AnnotatedBeanDefinitionReader} is used to register the appropriate
|
||||
* bean definitions.
|
||||
*
|
||||
* @param context the context in which the annotated classes should be registered
|
||||
* @param webMergedConfig the merged configuration from which the classes should be retrieved
|
||||
*
|
||||
* @see AbstractGenericWebContextLoader#loadBeanDefinitions
|
||||
*/
|
||||
@Override
|
||||
protected void loadBeanDefinitions(GenericWebApplicationContext context,
|
||||
WebMergedContextConfiguration webMergedConfig) {
|
||||
Class<?>[] annotatedClasses = webMergedConfig.getClasses();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Registering annotated classes: " + ObjectUtils.nullSafeToString(annotatedClasses));
|
||||
}
|
||||
new AnnotatedBeanDefinitionReader(context).register(annotatedClasses);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,263 +16,32 @@
|
|||
|
||||
package org.springframework.test.context.support;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.SmartContextLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* {@code DelegatingSmartContextLoader} is an implementation of the {@link SmartContextLoader}
|
||||
* SPI that delegates to a set of <em>candidate</em> SmartContextLoaders (i.e.,
|
||||
* {@link GenericXmlContextLoader} and {@link AnnotationConfigContextLoader}) to
|
||||
* determine which context loader is appropriate for a given test class's configuration.
|
||||
* Each candidate is given a chance to {@link #processContextConfiguration process} the
|
||||
* {@link ContextConfigurationAttributes} for each class in the test class hierarchy that
|
||||
* is annotated with {@link ContextConfiguration @ContextConfiguration}, and the candidate
|
||||
* that supports the merged, processed configuration will be used to actually
|
||||
* {@link #loadContext load} the context.
|
||||
*
|
||||
* <p>Placing an empty {@code @ContextConfiguration} annotation on a test class signals
|
||||
* that default resource locations (i.e., XML configuration files) or default
|
||||
* {@link org.springframework.context.annotation.Configuration configuration classes}
|
||||
* should be detected. Furthermore, if a specific {@link ContextLoader} or
|
||||
* {@link SmartContextLoader} is not explicitly declared via
|
||||
* {@code @ContextConfiguration}, {@code DelegatingSmartContextLoader} will be used as
|
||||
* the default loader, thus providing automatic support for either XML configuration
|
||||
* files or annotated classes, but not both simultaneously.
|
||||
*
|
||||
* <p>As of Spring 3.2, a test class may optionally declare neither XML configuration
|
||||
* files nor annotated classes and instead declare only {@linkplain
|
||||
* ContextConfiguration#initializers application context initializers}. In such
|
||||
* cases, an attempt will still be made to detect defaults, but their absence will
|
||||
* not result an an exception.
|
||||
* {@code DelegatingSmartContextLoader} is a concrete implementation of
|
||||
* {@link AbstractDelegatingSmartContextLoader} that delegates to a
|
||||
* {@link GenericXmlContextLoader} and an {@link AnnotationConfigContextLoader}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.1
|
||||
* @see SmartContextLoader
|
||||
* @see AbstractDelegatingSmartContextLoader
|
||||
* @see GenericXmlContextLoader
|
||||
* @see AnnotationConfigContextLoader
|
||||
*/
|
||||
public class DelegatingSmartContextLoader implements SmartContextLoader {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(DelegatingSmartContextLoader.class);
|
||||
public class DelegatingSmartContextLoader extends AbstractDelegatingSmartContextLoader {
|
||||
|
||||
private final SmartContextLoader xmlLoader = new GenericXmlContextLoader();
|
||||
private final SmartContextLoader annotationConfigLoader = new AnnotationConfigContextLoader();
|
||||
|
||||
|
||||
// --- SmartContextLoader --------------------------------------------------
|
||||
|
||||
private static String name(SmartContextLoader loader) {
|
||||
return loader.getClass().getSimpleName();
|
||||
protected SmartContextLoader getXmlLoader() {
|
||||
return this.xmlLoader;
|
||||
}
|
||||
|
||||
private static void delegateProcessing(SmartContextLoader loader, ContextConfigurationAttributes configAttributes) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Delegating to %s to process context configuration %s.", name(loader),
|
||||
configAttributes));
|
||||
}
|
||||
loader.processContextConfiguration(configAttributes);
|
||||
}
|
||||
|
||||
private static ApplicationContext delegateLoading(SmartContextLoader loader, MergedContextConfiguration mergedConfig)
|
||||
throws Exception {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Delegating to %s to load context from %s.", name(loader), mergedConfig));
|
||||
}
|
||||
return loader.loadContext(mergedConfig);
|
||||
}
|
||||
|
||||
private static boolean supports(SmartContextLoader loader, MergedContextConfiguration mergedConfig) {
|
||||
if (loader instanceof AnnotationConfigContextLoader) {
|
||||
return ObjectUtils.isEmpty(mergedConfig.getLocations()) && !ObjectUtils.isEmpty(mergedConfig.getClasses());
|
||||
} else {
|
||||
return !ObjectUtils.isEmpty(mergedConfig.getLocations()) && ObjectUtils.isEmpty(mergedConfig.getClasses());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to candidate {@code SmartContextLoaders} to process the supplied
|
||||
* {@link ContextConfigurationAttributes}.
|
||||
*
|
||||
* <p>Delegation is based on explicit knowledge of the implementations of
|
||||
* {@link GenericXmlContextLoader} and {@link AnnotationConfigContextLoader}.
|
||||
* Specifically, the delegation algorithm is as follows:
|
||||
*
|
||||
* <ul>
|
||||
* <li>If the resource locations or annotated classes in the supplied
|
||||
* {@code ContextConfigurationAttributes} are not empty, the appropriate
|
||||
* candidate loader will be allowed to process the configuration <em>as is</em>,
|
||||
* without any checks for detection of defaults.</li>
|
||||
* <li>Otherwise, {@code GenericXmlContextLoader} will be allowed to process
|
||||
* the configuration in order to detect default resource locations. If
|
||||
* {@code GenericXmlContextLoader} detects default resource locations,
|
||||
* an {@code info} message will be logged.</li>
|
||||
* <li>Subsequently, {@code AnnotationConfigContextLoader} will be allowed to
|
||||
* process the configuration in order to detect default configuration classes.
|
||||
* If {@code AnnotationConfigContextLoader} detects default configuration
|
||||
* classes, an {@code info} message will be logged.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param configAttributes the context configuration attributes to process
|
||||
* @throws IllegalArgumentException if the supplied configuration attributes are
|
||||
* <code>null</code>, or if the supplied configuration attributes include both
|
||||
* resource locations and annotated classes
|
||||
* @throws IllegalStateException if {@code GenericXmlContextLoader} detects default
|
||||
* configuration classes; if {@code AnnotationConfigContextLoader} detects default
|
||||
* resource locations; if neither candidate loader detects defaults for the supplied
|
||||
* context configuration; or if both candidate loaders detect defaults for the
|
||||
* supplied context configuration
|
||||
*/
|
||||
public void processContextConfiguration(final ContextConfigurationAttributes configAttributes) {
|
||||
|
||||
Assert.notNull(configAttributes, "configAttributes must not be null");
|
||||
Assert.isTrue(!(configAttributes.hasLocations() && configAttributes.hasClasses()), String.format(
|
||||
"Cannot process locations AND classes for context "
|
||||
+ "configuration %s; configure one or the other, but not both.", configAttributes));
|
||||
|
||||
// If the original locations or classes were not empty, there's no
|
||||
// need to bother with default detection checks; just let the
|
||||
// appropriate loader process the configuration.
|
||||
if (configAttributes.hasLocations()) {
|
||||
delegateProcessing(xmlLoader, configAttributes);
|
||||
} else if (configAttributes.hasClasses()) {
|
||||
delegateProcessing(annotationConfigLoader, configAttributes);
|
||||
} else {
|
||||
// Else attempt to detect defaults...
|
||||
|
||||
// Let the XML loader process the configuration.
|
||||
delegateProcessing(xmlLoader, configAttributes);
|
||||
boolean xmlLoaderDetectedDefaults = configAttributes.hasLocations();
|
||||
|
||||
if (xmlLoaderDetectedDefaults) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format("%s detected default locations for context configuration %s.",
|
||||
name(xmlLoader), configAttributes));
|
||||
}
|
||||
}
|
||||
|
||||
if (configAttributes.hasClasses()) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"%s should NOT have detected default configuration classes for context configuration %s.",
|
||||
name(xmlLoader), configAttributes));
|
||||
}
|
||||
|
||||
// Now let the annotation config loader process the configuration.
|
||||
delegateProcessing(annotationConfigLoader, configAttributes);
|
||||
|
||||
if (configAttributes.hasClasses()) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(String.format(
|
||||
"%s detected default configuration classes for context configuration %s.",
|
||||
name(annotationConfigLoader), configAttributes));
|
||||
}
|
||||
}
|
||||
|
||||
if (!xmlLoaderDetectedDefaults && configAttributes.hasLocations()) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"%s should NOT have detected default locations for context configuration %s.",
|
||||
name(annotationConfigLoader), configAttributes));
|
||||
}
|
||||
|
||||
// If neither loader detected defaults and no initializers were declared,
|
||||
// throw an exception.
|
||||
if (!configAttributes.hasResources() && ObjectUtils.isEmpty(configAttributes.getInitializers())) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Neither %s nor %s was able to detect defaults, and no ApplicationContextInitializers "
|
||||
+ "were declared for context configuration %s", name(xmlLoader),
|
||||
name(annotationConfigLoader), configAttributes));
|
||||
}
|
||||
|
||||
if (configAttributes.hasLocations() && configAttributes.hasClasses()) {
|
||||
String message = String.format(
|
||||
"Configuration error: both default locations AND default configuration classes "
|
||||
+ "were detected for context configuration %s; configure one or the other, but not both.",
|
||||
configAttributes);
|
||||
logger.error(message);
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates to an appropriate candidate {@code SmartContextLoader} to load
|
||||
* an {@link ApplicationContext}.
|
||||
*
|
||||
* <p>Delegation is based on explicit knowledge of the implementations of
|
||||
* {@link GenericXmlContextLoader} and {@link AnnotationConfigContextLoader}.
|
||||
* Specifically, the delegation algorithm is as follows:
|
||||
*
|
||||
* <ul>
|
||||
* <li>If the resource locations in the supplied {@code MergedContextConfiguration}
|
||||
* are not empty and the annotated classes are empty,
|
||||
* {@code GenericXmlContextLoader} will load the {@code ApplicationContext}.</li>
|
||||
* <li>If the annotated classes in the supplied {@code MergedContextConfiguration}
|
||||
* are not empty and the resource locations are empty,
|
||||
* {@code AnnotationConfigContextLoader} will load the {@code ApplicationContext}.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param mergedConfig the merged context configuration to use to load the application context
|
||||
* @throws IllegalArgumentException if the supplied merged configuration is <code>null</code>
|
||||
* @throws IllegalStateException if neither candidate loader is capable of loading an
|
||||
* {@code ApplicationContext} from the supplied merged context configuration
|
||||
*/
|
||||
public ApplicationContext loadContext(MergedContextConfiguration mergedConfig) throws Exception {
|
||||
Assert.notNull(mergedConfig, "mergedConfig must not be null");
|
||||
|
||||
List<SmartContextLoader> candidates = Arrays.asList(xmlLoader, annotationConfigLoader);
|
||||
|
||||
for (SmartContextLoader loader : candidates) {
|
||||
// Determine if each loader can load a context from the mergedConfig. If it
|
||||
// can, let it; otherwise, keep iterating.
|
||||
if (supports(loader, mergedConfig)) {
|
||||
return delegateLoading(loader, mergedConfig);
|
||||
}
|
||||
}
|
||||
|
||||
// If neither of the candidates supports the mergedConfig based on resources but
|
||||
// ACIs were declared, then delegate to the ACCL.
|
||||
if (!mergedConfig.getContextInitializerClasses().isEmpty()) {
|
||||
return delegateLoading(annotationConfigLoader, mergedConfig);
|
||||
}
|
||||
|
||||
throw new IllegalStateException(String.format(
|
||||
"Neither %s nor %s was able to load an ApplicationContext from %s.", name(xmlLoader),
|
||||
name(annotationConfigLoader), mergedConfig));
|
||||
}
|
||||
|
||||
// --- ContextLoader -------------------------------------------------------
|
||||
|
||||
/**
|
||||
* {@code DelegatingSmartContextLoader} does not support the
|
||||
* {@link ContextLoader#processLocations(Class, String...)} method. Call
|
||||
* {@link #processContextConfiguration(ContextConfigurationAttributes)} instead.
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
public String[] processLocations(Class<?> clazz, String... locations) {
|
||||
throw new UnsupportedOperationException("DelegatingSmartContextLoader does not support the ContextLoader SPI. "
|
||||
+ "Call processContextConfiguration(ContextConfigurationAttributes) instead.");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code DelegatingSmartContextLoader} does not support the
|
||||
* {@link ContextLoader#loadContext(String...) } method. Call
|
||||
* {@link #loadContext(MergedContextConfiguration)} instead.
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
public ApplicationContext loadContext(String... locations) throws Exception {
|
||||
throw new UnsupportedOperationException("DelegatingSmartContextLoader does not support the ContextLoader SPI. "
|
||||
+ "Call loadContext(MergedContextConfiguration) instead.");
|
||||
protected SmartContextLoader getAnnotationConfigLoader() {
|
||||
return this.annotationConfigLoader;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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.context.support;
|
||||
|
||||
import org.springframework.test.context.SmartContextLoader;
|
||||
|
||||
/**
|
||||
* {@code WebDelegatingSmartContextLoader} is a concrete implementation of
|
||||
* {@link AbstractDelegatingSmartContextLoader} that delegates to an
|
||||
* {@link XmlWebContextLoader} and an {@link AnnotationConfigWebContextLoader}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.2
|
||||
* @see SmartContextLoader
|
||||
* @see AbstractDelegatingSmartContextLoader
|
||||
* @see XmlWebContextLoader
|
||||
* @see AnnotationConfigWebContextLoader
|
||||
*/
|
||||
public class WebDelegatingSmartContextLoader extends AbstractDelegatingSmartContextLoader {
|
||||
|
||||
private final SmartContextLoader xmlLoader = new XmlWebContextLoader();
|
||||
private final SmartContextLoader annotationConfigLoader = new AnnotationConfigWebContextLoader();
|
||||
|
||||
|
||||
protected SmartContextLoader getXmlLoader() {
|
||||
return this.xmlLoader;
|
||||
}
|
||||
|
||||
protected SmartContextLoader getAnnotationConfigLoader() {
|
||||
return this.annotationConfigLoader;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.context.support;
|
||||
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.test.context.web.WebMergedContextConfiguration;
|
||||
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document XmlWebContextLoader.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.2
|
||||
*/
|
||||
public class XmlWebContextLoader extends AbstractGenericWebContextLoader {
|
||||
|
||||
/**
|
||||
* Returns "<code>-context.xml</code>".
|
||||
*/
|
||||
protected String getResourceSuffix() {
|
||||
return "-context.xml";
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document overridden loadBeanDefinitions().
|
||||
*
|
||||
* @see org.springframework.test.context.support.AbstractGenericWebContextLoader#loadBeanDefinitions(org.springframework.web.context.support.GenericWebApplicationContext, org.springframework.test.context.web.WebMergedContextConfiguration)
|
||||
*/
|
||||
@Override
|
||||
protected void loadBeanDefinitions(GenericWebApplicationContext context,
|
||||
WebMergedContextConfiguration webMergedConfig) {
|
||||
new XmlBeanDefinitionReader(context).loadBeanDefinitions(webMergedConfig.getLocations());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.context.web;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document WebAppConfiguration.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.2
|
||||
*/
|
||||
@Documented
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface WebAppConfiguration {
|
||||
|
||||
/**
|
||||
* The root directory of the web application (i.e., WAR); should not end with a slash.
|
||||
*
|
||||
* <p>Defaults to {@code "src/main/webapp"}.
|
||||
*/
|
||||
String value() default "src/main/webapp";
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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.context.web;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document WebMergedContextConfiguration.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.2
|
||||
*/
|
||||
public class WebMergedContextConfiguration extends MergedContextConfiguration {
|
||||
|
||||
private static final long serialVersionUID = 7323361588604247458L;
|
||||
|
||||
private final String resourceBasePath;
|
||||
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document WebMergedContextConfiguration constructor.
|
||||
*/
|
||||
public WebMergedContextConfiguration(
|
||||
Class<?> testClass,
|
||||
String[] locations,
|
||||
Class<?>[] classes,
|
||||
Set<Class<? extends ApplicationContextInitializer<? extends ConfigurableApplicationContext>>> contextInitializerClasses,
|
||||
String[] activeProfiles, String resourceBasePath, ContextLoader contextLoader) {
|
||||
|
||||
super(testClass, locations, classes, contextInitializerClasses, activeProfiles, contextLoader);
|
||||
|
||||
this.resourceBasePath = !StringUtils.hasText(resourceBasePath) ? "" : resourceBasePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document getResourceBasePath().
|
||||
*/
|
||||
public String getResourceBasePath() {
|
||||
return this.resourceBasePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique hash code for all properties of this
|
||||
* {@code WebMergedContextConfiguration} excluding the
|
||||
* {@linkplain #getTestClass() test class}.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + resourceBasePath.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the supplied object is equal to this {@code WebMergedContextConfiguration}
|
||||
* instance by comparing both object's {@linkplain #getLocations() locations},
|
||||
* {@linkplain #getClasses() annotated classes},
|
||||
* {@linkplain #getContextInitializerClasses() context initializer classes},
|
||||
* {@linkplain #getActiveProfiles() active profiles},
|
||||
* {@linkplain #getResourceBasePath() resource base path}, and the fully
|
||||
* qualified names of their {@link #getContextLoader() ContextLoaders}.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof WebMergedContextConfiguration)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final WebMergedContextConfiguration that = (WebMergedContextConfiguration) obj;
|
||||
|
||||
return super.equals(that) && this.getResourceBasePath().equals(that.getResourceBasePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a String representation of the {@linkplain #getTestClass() test class},
|
||||
* {@linkplain #getLocations() locations}, {@linkplain #getClasses() annotated classes},
|
||||
* {@linkplain #getContextInitializerClasses() context initializer classes},
|
||||
* {@linkplain #getActiveProfiles() active profiles},
|
||||
* {@linkplain #getResourceBasePath() resource base path}, and the name of the
|
||||
* {@link #getContextLoader() ContextLoader}.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this)//
|
||||
.append("testClass", getTestClass())//
|
||||
.append("locations", ObjectUtils.nullSafeToString(getLocations()))//
|
||||
.append("classes", ObjectUtils.nullSafeToString(getClasses()))//
|
||||
.append("contextInitializerClasses", ObjectUtils.nullSafeToString(getContextInitializerClasses()))//
|
||||
.append("activeProfiles", ObjectUtils.nullSafeToString(getActiveProfiles()))//
|
||||
.append("resourceBasePath", getResourceBasePath())//
|
||||
.append("contextLoader", nullSafeToString(getContextLoader()))//
|
||||
.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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.context.web;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.test.context.TestContext;
|
||||
import org.springframework.test.context.support.AbstractTestExecutionListener;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document WebTestExecutionListener.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.2
|
||||
*/
|
||||
public class WebTestExecutionListener extends AbstractTestExecutionListener {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(WebTestExecutionListener.class);
|
||||
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document overridden prepareTestInstance().
|
||||
*
|
||||
* @see org.springframework.test.context.support.AbstractTestExecutionListener#prepareTestInstance(org.springframework.test.context.TestContext)
|
||||
*/
|
||||
@Override
|
||||
public void prepareTestInstance(TestContext testContext) throws Exception {
|
||||
setUpRequestContextIfNecessary(testContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document overridden beforeTestMethod().
|
||||
*
|
||||
* @see org.springframework.test.context.support.AbstractTestExecutionListener#beforeTestMethod(org.springframework.test.context.TestContext)
|
||||
*/
|
||||
@Override
|
||||
public void beforeTestMethod(TestContext testContext) throws Exception {
|
||||
setUpRequestContextIfNecessary(testContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document setUpRequestContext().
|
||||
*
|
||||
* @param testContext
|
||||
* @param servletContext
|
||||
*/
|
||||
private void setUpRequestContextIfNecessary(TestContext testContext) {
|
||||
|
||||
ApplicationContext context = testContext.getApplicationContext();
|
||||
|
||||
if (context instanceof WebApplicationContext) {
|
||||
WebApplicationContext wac = (WebApplicationContext) context;
|
||||
ServletContext servletContext = wac.getServletContext();
|
||||
if (!(servletContext instanceof MockServletContext)) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"The WebApplicationContext for test context %s must be configured with a MockServletContext.",
|
||||
testContext));
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format(
|
||||
"Setting up MockHttpServletRequest, MockHttpServletResponse, ServletWebRequest, and RequestContextHolder for test context %s.",
|
||||
testContext));
|
||||
}
|
||||
|
||||
if (RequestContextHolder.getRequestAttributes() == null) {
|
||||
MockServletContext mockServletContext = (MockServletContext) servletContext;
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(mockServletContext);
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
ServletWebRequest servletWebRequest = new ServletWebRequest(request, response);
|
||||
|
||||
RequestContextHolder.setRequestAttributes(servletWebRequest);
|
||||
|
||||
if (wac instanceof ConfigurableApplicationContext) {
|
||||
ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) wac;
|
||||
ConfigurableListableBeanFactory bf = configurableApplicationContext.getBeanFactory();
|
||||
bf.registerResolvableDependency(MockHttpServletResponse.class, response);
|
||||
bf.registerResolvableDependency(ServletWebRequest.class, servletWebRequest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO [SPR-5243] Document overridden afterTestMethod().
|
||||
*
|
||||
* @see org.springframework.test.context.support.AbstractTestExecutionListener#afterTestMethod(org.springframework.test.context.TestContext)
|
||||
*/
|
||||
@Override
|
||||
public void afterTestMethod(TestContext testContext) throws Exception {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Resetting RequestContextHolder for test context %s.", testContext));
|
||||
}
|
||||
RequestContextHolder.resetRequestAttributes();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -42,7 +42,7 @@ public class TestExecutionListenersTests {
|
|||
@Test
|
||||
public void verifyNumDefaultListenersRegistered() throws Exception {
|
||||
TestContextManager testContextManager = new TestContextManager(DefaultListenersExampleTestCase.class);
|
||||
assertEquals("Verifying the number of registered TestExecutionListeners for DefaultListenersExampleTest.", 3,
|
||||
assertEquals("Verifying the number of registered TestExecutionListeners for DefaultListenersExampleTest.", 4,
|
||||
testContextManager.getTestExecutionListeners().size());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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.context.web;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.context.ServletContextAware;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@WebAppConfiguration
|
||||
public abstract class AbstractBasicWacTests implements ServletContextAware {
|
||||
|
||||
protected ServletContext servletContext;
|
||||
|
||||
@Autowired
|
||||
protected WebApplicationContext wac;
|
||||
|
||||
@Autowired
|
||||
protected MockServletContext mockServletContext;
|
||||
|
||||
@Autowired
|
||||
protected MockHttpServletRequest request;
|
||||
|
||||
@Autowired
|
||||
protected MockHttpServletResponse response;
|
||||
|
||||
@Autowired
|
||||
protected ServletWebRequest webRequest;
|
||||
|
||||
@Autowired
|
||||
protected String foo;
|
||||
|
||||
|
||||
public void setServletContext(ServletContext servletContext) {
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicWacFeatures() throws Exception {
|
||||
assertNotNull("ServletContext should be set in the WAC.", wac.getServletContext());
|
||||
|
||||
assertNotNull("ServletContext should have been set via ServletContextAware.", servletContext);
|
||||
|
||||
assertNotNull("ServletContext should have been autowired from the WAC.", mockServletContext);
|
||||
assertNotNull("MockHttpServletRequest should have been autowired from the WAC.", request);
|
||||
assertNotNull("MockHttpServletResponse should have been autowired from the WAC.", response);
|
||||
assertNotNull("ServletWebRequest should have been autowired from the WAC.", webRequest);
|
||||
|
||||
Object rootWac = mockServletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
|
||||
assertNotNull("Root WAC must be stored in the ServletContext as: "
|
||||
+ WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, rootWac);
|
||||
assertSame("test WAC and Root WAC in ServletContext must be the same object.", wac, rootWac);
|
||||
assertSame("ServletContext instances must be the same object.", mockServletContext, wac.getServletContext());
|
||||
assertSame("ServletContext in the WAC and in the mock request", mockServletContext, request.getServletContext());
|
||||
|
||||
assertEquals("Getting real path for ServletContext resource.",
|
||||
new File("src/main/webapp/index.jsp").getCanonicalPath(), mockServletContext.getRealPath("index.jsp"));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.context.web;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2
|
||||
*/
|
||||
@ContextConfiguration
|
||||
public class BasicAnnotationConfigWacTests extends AbstractBasicWacTests {
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public String foo() {
|
||||
return "enigma";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void fooEnigmaAutowired() {
|
||||
assertEquals("enigma", foo);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -13,12 +13,24 @@
|
|||
* 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 {
|
||||
package org.springframework.test.context.web;
|
||||
|
||||
public WebContextLoader() {
|
||||
super("src/test/resources/META-INF/web-resources", false);
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 3.2
|
||||
*/
|
||||
@ContextConfiguration
|
||||
public class BasicXmlWacTests extends AbstractBasicWacTests {
|
||||
|
||||
@Test
|
||||
public void fooBarAutowired() {
|
||||
assertEquals("bar", foo);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.context.web;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
import org.junit.runners.Suite.SuiteClasses;
|
||||
import org.springframework.test.context.ContextLoader;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
/**
|
||||
* Convenience test suite for integration tests that verify support for
|
||||
* {@link WebApplicationContext} {@linkplain ContextLoader context loaders}
|
||||
* in the TestContext framework.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 3.2
|
||||
*/
|
||||
@RunWith(Suite.class)
|
||||
// Note: the following 'multi-line' layout is for enhanced code readability.
|
||||
@SuiteClasses({//
|
||||
BasicXmlWacTests.class,//
|
||||
BasicAnnotationConfigWacTests.class //
|
||||
})
|
||||
public class WebContextLoaderTestSuite {
|
||||
}
|
||||
|
|
@ -45,6 +45,10 @@
|
|||
<level value="warn" />
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework.test.context.web">
|
||||
<level value="warn" />
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework.test.context">
|
||||
<level value="warn" />
|
||||
</logger>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<bean id="foo" class="java.lang.String" c:_="bar" />
|
||||
|
||||
</beans>
|
||||
Loading…
Reference in New Issue