diff --git a/src/reference/docbook/mvc.xml b/src/reference/docbook/mvc.xml
index 24b5c877d7f..2363b808ea7 100644
--- a/src/reference/docbook/mvc.xml
+++ b/src/reference/docbook/mvc.xml
@@ -2358,6 +2358,15 @@ public String myHandleMethod(WebRequest webRequest, Model model) {
request.
+
+
+ Testing Controllers
+
+ The spring-test module offers first class support
+ for testing annotated controllers.
+ See .
+
+
diff --git a/src/reference/docbook/new-in-3.2.xml b/src/reference/docbook/new-in-3.2.xml
index c24fd5c0cc5..19a1f9dc9d0 100644
--- a/src/reference/docbook/new-in-3.2.xml
+++ b/src/reference/docbook/new-in-3.2.xml
@@ -49,10 +49,7 @@
fluent API and without a servlet container. Server-side tests involve
use of the DispatcherServlet while client-side
REST tests rely on the RestTemplate.
- See the following presentation for more information before
- documentation is added:
-
- "Testing Web Applications with Spring 3.2".
+ See .
diff --git a/src/reference/docbook/testing.xml b/src/reference/docbook/testing.xml
index dd421856978..93ffe9e8b63 100644
--- a/src/reference/docbook/testing.xml
+++ b/src/reference/docbook/testing.xml
@@ -2399,6 +2399,531 @@ public class SimpleTest {
+
+ Spring MVC Test Framework
+
+
+ Standalone project
+
+ Before inclusion in Spring Framework 3.2, the Spring MVC Test
+ framework had already existed as a separate project on Github where
+ it grew and evolved through actual use, feedback, and the contribution of
+ many.
+ The standalone
+ spring-test-mvc project
+ is still available on Github and can be used in conjunction with
+ Spring Framework 3.1.x. Applications upgrading to 3.2 should replace
+ the spring-test-mvc dependency with a dependency on
+ spring-test.
+
+ The spring-test module uses a
+ different package org.springframework.test.web
+ but otherwise is nearly identical with two exceptions.
+ One is support for features new in 3.2 (e.g. async web requests).
+ The other relates to the options
+ for creating a MockMvc instance. In Spring
+ Framework 3.2, this can only be done through the TestContext framework,
+ which provides caching benefits for the loaded configuration.
+
+
+ The Spring MVC Test framework provides
+ first class JUnit support for testing client and server-side
+ Spring MVC code through a fluent API. Typically it loads
+ the actual Spring configuration through the
+ TestContext framework and always uses the
+ DispatcherServlet to process requests
+ thus approximating full integration tests without requiring
+ a running servlet container.
+
+ Client-side tests are
+ RestTemplate-based and allow tests for code
+ that relies on the RestTemplate without requiring a
+ running server to respond to the requests.
+
+
+
+ Server-Side Tests
+
+ Before Spring Framework 3.2, the most likely way to test a Spring MVC
+ controller was to write a unit test that instantiates the controller,
+ injects it with mock or stub dependencies, and then calls its methods
+ directly, using a MockHttpServletRequest and
+ MockHttpServletResponse where necessary.
+ Although this is pretty easy to do, controllers have many annotations,
+ and much remains not tested. Request mappings, data binding,
+ type conversion, and validation are just a few examples of what isn't tested.
+ Furthermore, there are other types of annotated methods
+ such as @InitBinder,
+ @ModelAttribute, and
+ @ExceptionHandler that get invoked as part
+ of request processing.
+
+ The idea behind Spring MVC Test is to be able to
+ re-write those controller tests by performing actual requests, and generating
+ responses, as they would be at runtime, along the way invoking controllers
+ through the Spring MVC DispatcherServlet. Controllers
+ can still be injected with mock dependencies, so tests can remain focused
+ on the web layer.
+
+ Spring MVC Test builds on the familiar "mock" implementations
+ of the Servlet API available in the spring-test module.
+ This allows performing requests and generating responses without
+ the need for running in a Servlet container. For the most part everything should
+ work as it does at runtime with the exception of JSP rendering, which is
+ not available outside a Servlet container. Furthermore,
+ if you are familiar with how the MockHttpServletResponse
+ works, you'll know that forwards and redirects are not actually executed.
+ Instead "forwarded" and "redirected" URLs are saved and can be asserted
+ in tests. This means if using JSPs, you can verify the JSP page
+ the request was forwarded to.
+
+ All other means of rendering including
+ @ResponseBody methods and
+ View types (besides JSPs) such as
+ Freemarker, Velocity, Thymeleaf, and others for rendering HTML,
+ JSON, XML, and so on should work as expected and the response will
+ contain the generated content.
+
+
+ Below is an example of a test requesting account information in JSON format:
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@WebAppConfiguration
+@ContextConfiguration("test-servlet-context.xml")
+public class ExampleTests {
+
+ @Autowired
+ private WebApplicationContext wac;
+
+ private MockMvc mockMvc;
+
+ @Before
+ public void setup() {
+ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
+ }
+
+ @Test
+ public void getAccount() throws Exception {
+ this.mockMvc.perform(get("/accounts/1").accept("application/json;charset=UTF-8"))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType("application/json"))
+ .andExpect(jsonPath("$.name").value("Lee");
+ }
+
+}
+
+ The test relies on the WebApplicationContext
+ support of the TestContext framework. It loads Spring configuration from
+ an XML config file located in the same
+ package as the test class (also supports Java config) and injects
+ the created WebApplicationContext
+ into the test so a MockMvc instance can be
+ created with it.
+
+ The MockMvc is then
+ used to perform a request to "/accounts/1"
+ and verify the resulting
+ response status is 200, the response content type is
+ "application/json", and response content
+ has a JSON property called "name" with the value "Lee".
+ JSON content is inspected with the help of Jayway's
+ JsonPath project.
+ There are lots of other options for verifying the
+ result of the performed request and those will be discussed later.
+
+
+ Static Imports
+
+ The fluent API in the example above requires a few static imports
+ such as MockMvcRequestBuilders.*,
+ MockMvcResultMatchers.*, and
+ MockMvcBuilders.*. An easy way to find
+ these classes is to search for types matching
+ "MockMvc*".
+ If using Eclipse, be sure to add them as
+ "favorite static members" in the Eclipse preferences under
+ Java -> Editor -> Content Assist -> Favorites.
+ That will allow use of content assist after typing
+ the first character of the static method name.
+ Other IDEs (e.g. IntelliJ) may not require any additional
+ configuration. Just check the support for code completion
+ on static members.
+
+
+
+
+ Setup Options
+
+ The goal of server-side test setup is to create
+ an instance of MockMvc that can be used
+ to perform requests. There are two main options.
+
+ The first option is to point to Spring MVC configuration
+ through the TestContext framework,
+ which loads the Spring configuration and injects a
+ WebApplicationContext into the
+ test to use to create a MockMvc:
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@WebAppConfiguration
+@ContextConfiguration("my-servlet-context.xml")
+public class MyWebTests {
+
+ @Autowired
+ private WebApplicationContext wac;
+
+ private MockMvc mockMvc;
+
+ @Before
+ public void setup() {
+ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
+ }
+
+ // ...
+
+}
+
+ The second option is to simply register a controller
+ instance without loading any Spring configuration.
+ Instead basic Spring MVC configuration suitable for testing
+ annotated controllers is automatically created. The created
+ configuration is comparable to that of the MVC Java config
+ (and the MVC namespace) and can be customized to a degree
+ through builder-style methods:
+
+public class MyWebTests {
+
+ private MockMvc mockMvc;
+
+ @Before
+ public void setup() {
+ this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();
+ }
+
+ // ...
+
+}
+
+ Which option should you use?
+
+ The "webAppContextSetup" loads
+ the actual Spring MVC configuration
+ resulting in a more complete integration test. Since the
+ TestContext framework caches the loaded
+ Spring configuration, it helps to keep tests running fast
+ even as more tests get added. Furthermore, you can inject mock
+ services into controllers through Spring configuration,
+ in order to remain focused on testing the web layer.
+ Here is an example of declaring a mock service with Mockito:
+
+
+<bean id="accountService" class="org.mockito.Mockito" factory-method="mock">
+ <constructor-arg value="org.example.AccountService"/>
+</bean>
+
+
+ Then you can inject the mock service into the test
+ in order set up and verify expectations:
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@WebAppConfiguration
+@ContextConfiguration("test-servlet-context.xml")
+public class AccountTests {
+
+ @Autowired
+ private WebApplicationContext wac;
+
+ private MockMvc mockMvc;
+
+ @Autowired
+ private AccountService accountService;
+
+ // ...
+
+}
+
+ The "standaloneSetup" on the other
+ hand is a little closer to a unit test. It tests one controller at a time,
+ the controller can be injected with mock dependencies manually,
+ and it doesn't involve loading Spring configuration.
+ Such tests are more focused in style and make it easier to see
+ which controller is being tested, whether any specific Spring MVC
+ configuration is required to work, and so on. The "standaloneSetup" is also
+ a very convenient way to write ad-hoc tests to verify some
+ behavior or to debug an issue.
+
+ Just like with integration vs unit testing, there is no right or
+ wrong answer. Using the "standaloneSetup" does imply the
+ need for some additional "webAppContextSetup" tests to verify the
+ Spring MVC configuration. Alternatively, you can decide write all
+ tests with "webAppContextSetup" and always test against actual
+ Spring MVC configuration.
+
+
+
+ Performing Requests
+
+ To perform requests, use the appropriate HTTP
+ method and additional builder-style methods corresponding
+ to properties of MockHttpServletRequest.
+ For example:
+
+
+mockMvc.perform(post("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON));
+
+
+ In addition to all the HTTP methods, you can also perform file
+ upload requests, which internally creates an instance of
+ MockMultipartHttpServletRequest:
+
+
+mockMvc.perform(fileUpload("/doc").file("a1", "ABC".getBytes("UTF-8")));
+
+
+ Query string parameters can be specified in the URI template:
+
+
+mockMvc.perform(get("/hotels?foo={foo}", "bar"));
+
+
+ Or by adding Servlet request parameters:
+
+
+mockMvc.perform(get("/hotels").param("foo", "bar"));
+
+
+ If application code relies on Servlet request parameters, and
+ doesn't check the query string, as is most often the case,
+ then it doesn't matter how parameters are added. Keep in mind
+ though that parameters provided in the URI template will be decoded
+ while parameters provided through the param(...)
+ method are expected to be decoded.
+
+ In most cases it's preferable to leave out the context path and
+ the servlet path from the request URI. If you must test with the full
+ request URI, be sure to set the contextPath and servletPath
+ accordingly so that request mappings will work:
+
+
+mockMvc.perform(get("/app/main/hotels/{id}").contextPath("/app").servletPath("/main"))
+
+
+ Looking at the above example, it would be cumbersome to set
+ the contextPath and servletPath with every peformed request.
+ That's why you can define default request
+ properties when building the MockMvc:
+
+
+public class MyWebTests {
+
+ private MockMvc mockMvc;
+
+ @Before
+ public void setup() {
+ mockMvc = standaloneSetup(new AccountController())
+ .defaultRequest(get("/")
+ .contextPath("/app").servletPath("/main")
+ .accept(MediaType.APPLICATION_JSON).build();
+ }
+
+}
+
+ The above properties will apply to every request performed through
+ the MockMvc. If the same property is also specified
+ on a given request, it will override the default value. That is why, the
+ HTTP method and URI don't matter, when setting default request properties,
+ since they must be specified on every request.
+
+
+
+
+ Defining Expectations
+
+ Expectations can be defined by appending one or more
+ .andExpect(..) after call to perform the request:
+
+
+mockMvc.perform(get("/accounts/1")).andExpect(status().isOk());
+
+
+ MockMvcResultMatchers.* defines a number of static
+ members, some of which return types with additional methods, for
+ asserting the result of the performed request. The assertions
+ fall in two general categories.
+
+ The first category of assertions verify properties of the
+ response, i.e the response status, headers, and content.
+ Those are the most important things to test for.
+
+ The second category of assertions go beyond the response, and allow
+ inspecting Spring MVC specific constructs such as which controller
+ method processed the request, whether an exception was raised and handled,
+ what the content of the model is, what view was selected, what flash
+ attributes were added, and so on. It is also possible to
+ verify Servlet specific constructs such as request and session attributes.
+ The following test asserts that binding/validation failed:
+
+
+
+mockMvc.perform(post("/persons"))
+ .andExpect(status().isOk())
+ .andExpect(model().attributeHasErrors("person"));
+
+
+ Many times when writing tests, it's useful to dump the result
+ of the performed request. This can be done as follows:
+
+
+mockMvc.perform(post("/persons"))
+ .andDo(print())
+ .andExpect(status().isOk())
+ .andExpect(model().attributeHasErrors("person"));
+
+
+ where print() is a static import from
+ MockMvcResultHandlers. As long as request processing
+ does cause an unhandled exception, the print()
+ method will print all the available result data to System.out.
+
+ In some cases, you may want to get direct access to the result and
+ verify something that cannot be verified otherwise. This can be done by
+ appending .andReturn() at the end after all expectations:
+
+
+MvcResult mvcResult = mockMvc.perform(post("/persons")).andExpect(status().isOk()).andReturn();
+// ...
+
+
+ When all tests repeat the same expectations, you can define
+ the common expectations once when building the
+ MockMvc:
+
+
+standaloneSetup(new SimpleController())
+ .alwaysExpect(status().isOk())
+ .alwaysExpect(content().contentType("application/json;charset=UTF-8"))
+ .build()
+
+
+ Note that the expectation is always applied
+ and cannot be overridden without creating a separate
+ MockMvc instance.
+
+ When JSON response content contains hypermedia links created with
+ Spring HATEOAS,
+ the resulting links can be verified:
+
+
+mockMvc.perform(get("/people").accept(MediaType.APPLICATION_JSON))
+ .andExpect(jsonPath("$.links[?(@.rel == 'self')].href").value("http://localhost:8080/people"));
+
+
+ When XML response content contains hypermedia links created with
+ Spring HATEOAS,
+ the resulting links can be verified:
+
+
+Map<String, String> ns = Collections.singletonMap("ns", "http://www.w3.org/2005/Atom");
+mockMvc.perform(get("/handle").accept(MediaType.APPLICATION_XML))
+ .andExpect(xpath("/person/ns:link[@rel='self']/@href", ns).string("http://localhost:8080/people"));
+
+
+
+
+
+ Filter Registrations
+
+ When setting up a MockMvc, you can
+ register one or more Filter instances:
+
+
+mockMvc = standaloneSetup(new PersonController()).addFilters(new CharacterEncodingFilter()).build();
+
+
+ Registered filters will be invoked through
+ MockFilterChain from
+ spring-test and the last filter will delegates to
+ the DispatcherServlet.
+
+
+
+
+ Further Server-Side Test Examples
+
+ The framework's own tests include
+ many sample tests
+ intended to demonstrate how to use Spring MVC Test.
+ Browse these examples for further ideas. Also the
+ spring-mvc-showcase
+ has full test coverage based on Spring MVC Test.
+
+
+
+
+
+ Client-Side REST Tests
+
+ Client-side tests are for code using the RestTemplate.
+ The goal is to define expected requests and provide "stub" responses:
+
+
+RestTemplate restTemplate = new RestTemplate();
+
+MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
+mockServer.expect(requestTo("/greeting")).andRespond(withSuccess("Hello world", "text/plain"));
+
+// use RestTemplate ...
+
+mockServer.verify();
+
+
+ In the above example, MockRestServiceServer --
+ the central class for client-side REST tests -- configures the
+ RestTemplate with a custom
+ ClientHttpRequestFactory that asserts
+ actual requests against expectations and returns "stub" responses.
+ In this case we expect a single request to "/greeting" and want to return
+ a 200 response with "text/plain" content. We could define as many
+ additional requests and stub responses as necessary.
+
+ Once expected requests and stub
+ responses have been defined, the RestTemplate can
+ be used in client-side code as usual. At the end of the tests
+ mockServer.verify()
+ can be used to verify that all expected requests were performed.
+
+
+ Static Imports
+
+ Just like with server-side tests, the fluent API for client-side tests
+ requires a few static imports. Those are easy to find by searching
+ "MockRest*". Eclipse users should add
+ "MockRestRequestMatchers.*" and
+ "MockRestResponseCreators.*"
+ as "favorite static members" in the Eclipse preferences under
+ Java -> Editor -> Content Assist -> Favorites.
+ That allows using content assist after typing
+ the first character of the static method name.
+ Other IDEs (e.g. IntelliJ) may not require any additional
+ configuration. Just check the support for code completion
+ on static members.
+
+
+
+ Further Examples of Client-side REST Tests
+
+ Spring MVC Test's own tests include
+ example tests
+ of client-side REST tests.
+
+
+
+
+
+
PetClinic Example