Skip to content

Commit

Permalink
Support loading WebApplicationContexts in the TCF
Browse files Browse the repository at this point in the history
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
  • Loading branch information
sbrannen committed Oct 7, 2012
1 parent 3552173 commit a73280c
Show file tree
Hide file tree
Showing 29 changed files with 1,474 additions and 601 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,48 +16,47 @@

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;
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
private WebApplicationContext wac;

private MockMvc mockMvc;


@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}

@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"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -34,6 +35,7 @@
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;
Expand All @@ -44,32 +46,25 @@
/**
* 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;
Expand All @@ -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);
}
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,51 +16,47 @@

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;
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
private WebApplicationContext wac;

private MockMvc mockMvc;


@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}

@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"));
}

}

1 change: 1 addition & 0 deletions spring-test/.springBeans
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Loading

0 comments on commit a73280c

Please sign in to comment.