Spring MVC Test Framework

The Spring MVC Test framework provides first class support for testing Spring MVC code with a fluent API that you can use with JUnit, TestNG, or any other testing framework. It is built on the Servlet API mock objects from the spring-test module and, hence, does not use a running Servlet container. It uses the DispatcherServlet to provide full Spring MVC runtime behavior and provides support for loading actual Spring configuration with the TestContext framework in addition to a standalone mode, in which you can manually instantiate controllers and test them one at a time.

Spring MVC Test also provides client-side support for testing code that uses the RestTemplate. Client-side tests mock the server responses and also do not use a running server.

Spring Boot provides an option to write full, end-to-end integration tests that include a running server. If this is your goal, see the Spring Boot Reference Guide. For more information on the differences between out-of-container and end-to-end integration tests, see spring-mvc-test-vs-end-to-end-integration-tests.

Server-Side Tests

You can write a plain unit test for a Spring MVC controller by using JUnit or TestNG. To do so, instantiate the controller, inject it with mocked or stubbed dependencies, and call its methods (passing MockHttpServletRequest, MockHttpServletResponse, and others, as necessary). However, when writing such a unit test, much remains untested: for example, request mappings, data binding, type conversion, validation, and much more. Furthermore, other controller methods such as @InitBinder, @ModelAttribute, and @ExceptionHandler may also be invoked as part of the request processing lifecycle.

The goal of Spring MVC Test is to provide an effective way to test controllers by performing requests and generating responses through the actual DispatcherServlet.

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 a few notable exceptions, as explained in spring-mvc-test-vs-end-to-end-integration-tests. The following JUnit Jupiter-based example uses Spring MVC Test:

Java
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.;

@SpringJUnitWebConfig(locations = "test-servlet-context.xml")
class ExampleTests {

	MockMvc mockMvc;

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

	@Test
	void getAccount() throws Exception {
		this.mockMvc.perform(get("/accounts/1")
				.accept(MediaType.APPLICATION_JSON))
			.andExpect(status().isOk())
			.andExpect(content().contentType("application/json"))
			.andExpect(jsonPath("$.name").value("Lee"));
	}
}
Kotlin
import org.springframework.test.web.servlet.get

@SpringJUnitWebConfig(locations = ["test-servlet-context.xml"])
class ExampleTests {

	lateinit var mockMvc: MockMvc

	@BeforeEach
	fun setup(wac: WebApplicationContext) {
		this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build()
	}

	@Test
	fun getAccount() {
		mockMvc.get("/accounts/1") {
			accept = MediaType.APPLICATION_JSON
		}.andExpect {
			status { isOk }
			content { contentType(MediaType.APPLICATION_JSON) }
			jsonPath("$.name") { value("Lee") }
		}
	}
}
A dedicated MockMvc DSL is available in Kotlin

The preceding test relies on the WebApplicationContext support of the TestContext framework to load Spring configuration from an XML configuration file located in the same package as the test class, but Java-based and Groovy-based configuration are also supported. See these sample tests.

The MockMvc instance is used to perform a GET request to /accounts/1 and verify that the resulting response has status 200, the content type is application/json, and the response body has a JSON property called name with the value Lee. The jsonPath syntax is supported through the Jayway JsonPath project. Many other options for verifying the result of the performed request are discussed later in this document.

Static Imports

The fluent API in the example from the preceding section requires a few static imports, such as MockMvcRequestBuilders.*, MockMvcResultMatchers.*, and MockMvcBuilders.*. An easy way to find these classes is to search for types that match MockMvc*. If you use Eclipse or the Eclipse-based Spring Tool Suite, be sure to add them as “favorite static members” in the Eclipse preferences under Java → Editor → Content Assist → Favorites. Doing so lets you use content assist after typing the first character of the static method name. Other IDEs (such as IntelliJ) may not require any additional configuration. Check the support for code completion on static members.

Setup Choices

You have two main options for creating an instance of MockMvc. The first is to load Spring MVC configuration through the TestContext framework, which loads the Spring configuration and injects a WebApplicationContext into the test to use to build a MockMvc instance. The following example shows how to do so:

Java
@SpringJUnitWebConfig(locations = "my-servlet-context.xml")
class MyWebTests {

	MockMvc mockMvc;

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

	// ...

}
Kotlin
@SpringJUnitWebConfig(locations = ["my-servlet-context.xml"])
class MyWebTests {

	lateinit var mockMvc: MockMvc

	@BeforeEach
	fun setup(wac: WebApplicationContext) {
		mockMvc = MockMvcBuilders.webAppContextSetup(wac).build()
	}

	// ...

}

Your second option is to manually create a controller instance without loading Spring configuration. Instead, basic default configuration, roughly comparable to that of the MVC JavaConfig or the MVC namespace, is automatically created. You can customize it to a degree. The following example shows how to do so:

Java
class MyWebTests {

	MockMvc mockMvc;

	@BeforeEach
	void setup() {
		this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();
	}

	// ...

}
Kotlin
class MyWebTests {

	lateinit var mockMvc : MockMvc

	@BeforeEach
	fun setup() {
		mockMvc = MockMvcBuilders.standaloneSetup(AccountController()).build()
	}

	// ...

}

Which setup option should you use?

The webAppContextSetup loads your actual Spring MVC configuration, resulting in a more complete integration test. Since the TestContext framework caches the loaded Spring configuration, it helps keep tests running fast, even as you introduce more tests in your test suite. Furthermore, you can inject mock services into controllers through Spring configuration to remain focused on testing the web layer. The following example declares a mock service with Mockito:

<bean id="accountService" class="org.mockito.Mockito" factory-method="mock">
	<constructor-arg value="org.example.AccountService"/>
</bean>

You can then inject the mock service into the test to set up and verify your expectations, as the following example shows:

Java
@SpringJUnitWebConfig(locations = "test-servlet-context.xml")
class AccountTests {

	@Autowired
	AccountService accountService;

	MockMvc mockMvc;

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

	// ...

}
Kotlin
@SpringJUnitWebConfig(locations = ["test-servlet-context.xml"])
class AccountTests {

	@Autowired
	lateinit var accountService: AccountService

	lateinit mockMvc: MockMvc

	@BeforeEach
	fun setup(wac: WebApplicationContext) {
		mockMvc = MockMvcBuilders.webAppContextSetup(wac).build()
	}

	// ...

}

The standaloneSetup, on the other hand, is a little closer to a unit test. It tests one controller at a time. You can manually inject the controller with mock dependencies, and it does not involve loading Spring configuration. Such tests are more focused on 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 specific behavior or to debug an issue.

As with most “integration versus unit testing” debates, there is no right or wrong answer. However, using the standaloneSetup does imply the need for additional webAppContextSetup tests in order to verify your Spring MVC configuration. Alternatively, you can write all your tests with webAppContextSetup, in order to always test against your actual Spring MVC configuration.

Setup Features

No matter which MockMvc builder you use, all MockMvcBuilder implementations provide some common and very useful features. For example, you can declare an Accept header for all requests and expect a status of 200 as well as a Content-Type header in all responses, as follows:

Java
// static import of MockMvcBuilders.standaloneSetup

MockMvc mockMvc = standaloneSetup(new MusicController())
	.defaultRequest(get("/").accept(MediaType.APPLICATION_JSON))
	.alwaysExpect(status().isOk())
	.alwaysExpect(content().contentType("application/json;charset=UTF-8"))
	.build();
Kotlin
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed

In addition, third-party frameworks (and applications) can pre-package setup instructions, such as those in a MockMvcConfigurer. The Spring Framework has one such built-in implementation that helps to save and re-use the HTTP session across requests. You can use it as follows:

Java
// static import of SharedHttpSessionConfigurer.sharedHttpSession

MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new TestController())
		.apply(sharedHttpSession())
		.build();

// Use mockMvc to perform requests...
Kotlin
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed

See the javadoc for ConfigurableMockMvcBuilder for a list of all MockMvc builder features or use the IDE to explore the available options.

Performing Requests

You can perform requests that use any HTTP method, as the following example shows:

Java
mockMvc.perform(post("/hotels/{id}", 42).accept(MediaType.APPLICATION_JSON));
Kotlin
import org.springframework.test.web.servlet.post

mockMvc.post("/hotels/{id}", 42) {
	accept = MediaType.APPLICATION_JSON
}

You can also perform file upload requests that internally use MockMultipartHttpServletRequest so that there is no actual parsing of a multipart request. Rather, you have to set it up to be similar to the following example:

Java
mockMvc.perform(multipart("/doc").file("a1", "ABC".getBytes("UTF-8")));
Kotlin
import org.springframework.test.web.servlet.multipart

mockMvc.multipart("/doc") {
	file("a1", "ABC".toByteArray(charset("UTF8")))
}

You can specify query parameters in URI template style, as the following example shows:

Java
mockMvc.perform(get("/hotels?thing={thing}", "somewhere"));
Kotlin
mockMvc.get("/hotels?thing={thing}", "somewhere")

You can also add Servlet request parameters that represent either query or form parameters, as the following example shows:

Java
mockMvc.perform(get("/hotels").param("thing", "somewhere"));
Kotlin
import org.springframework.test.web.servlet.get

mockMvc.get("/hotels") {
	param("thing", "somewhere")
}

If application code relies on Servlet request parameters and does not check the query string explicitly (as is most often the case), it does not matter which option you use. Keep in mind, however, that query parameters provided with the URI template are decoded while request parameters provided through the param(…​) method are expected to already be decoded.

In most cases, it is preferable to leave the context path and the Servlet path out of 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 work, as the following example shows:

Java
mockMvc.perform(get("/app/main/hotels/{id}").contextPath("/app").servletPath("/main"))
Kotlin
import org.springframework.test.web.servlet.get

mockMvc.get("/app/main/hotels/{id}") {
	contextPath = "/app"
	servletPath = "/main"
}

In the preceding example, it would be cumbersome to set the contextPath and servletPath with every performed request. Instead, you can set up default request properties, as the following example shows:

Java
class MyWebTests {

	MockMvc mockMvc;

	@BeforeEach
	void setup() {
		mockMvc = standaloneSetup(new AccountController())
			.defaultRequest(get("/")
			.contextPath("/app").servletPath("/main")
			.accept(MediaType.APPLICATION_JSON)).build();
	}
}
Kotlin
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed

The preceding properties affect every request performed through the MockMvc instance. If the same property is also specified on a given request, it overrides the default value. That is why the HTTP method and URI in the default request do not matter, since they must be specified on every request.

Defining Expectations

You can define expectations by appending one or more .andExpect(..) calls after performing a request, as the following example shows:

Java
mockMvc.perform(get("/accounts/1")).andExpect(status().isOk());
Kotlin
import org.springframework.test.web.servlet.get

mockMvc.get("/accounts/1").andExpect {
	status().isOk()
}

MockMvcResultMatchers.* provides a number of expectations, some of which are further nested with more detailed expectations.

Expectations fall in two general categories. The first category of assertions verifies properties of the response (for example, the response status, headers, and content). These are the most important results to assert.

The second category of assertions goes beyond the response. These assertions let you inspect Spring MVC specific aspects, 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. They also let you inspect Servlet specific aspects, such as request and session attributes.

The following test asserts that binding or validation failed:

Java
mockMvc.perform(post("/persons"))
	.andExpect(status().isOk())
	.andExpect(model().attributeHasErrors("person"));
Kotlin
import org.springframework.test.web.servlet.post

mockMvc.post("/persons").andExpect {
	status().isOk()
	model {
		attributeHasErrors("person")
	}
}

Many times, when writing tests, it is useful to dump the results of the performed request. You can do so as follows, where print() is a static import from MockMvcResultHandlers:

Java
mockMvc.perform(post("/persons"))
	.andDo(print())
	.andExpect(status().isOk())
	.andExpect(model().attributeHasErrors("person"));
Kotlin
import org.springframework.test.web.servlet.post

mockMvc.post("/persons").andDo {
		print()
	}.andExpect {
		status().isOk()
		model {
			attributeHasErrors("person")
		}
	}

As long as request processing does not cause an unhandled exception, the print() method prints all the available result data to System.out. There is also a log() method and two additional variants of the print() method, one that accepts an OutputStream and one that accepts a Writer. For example, invoking print(System.err) prints the result data to System.err, while invoking print(myWriter) prints the result data to a custom writer. If you want to have the result data logged instead of printed, you can invoke the log() method, which logs the result data as a single DEBUG message under the org.springframework.test.web.servlet.result logging category.

In some cases, you may want to get direct access to the result and verify something that cannot be verified otherwise. This can be achieved by appending .andReturn() after all other expectations, as the following example shows:

Java
MvcResult mvcResult = mockMvc.perform(post("/persons")).andExpect(status().isOk()).andReturn();
// ...
Kotlin
var mvcResult = mockMvc.post("/persons").andExpect { status().isOk() }.andReturn()
// ...

If all tests repeat the same expectations, you can set up common expectations once when building the MockMvc instance, as the following example shows:

Java
standaloneSetup(new SimpleController())
	.alwaysExpect(status().isOk())
	.alwaysExpect(content().contentType("application/json;charset=UTF-8"))
	.build()
Kotlin
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed

Note that common expectations are always applied and cannot be overridden without creating a separate MockMvc instance.

When a JSON response content contains hypermedia links created with Spring HATEOAS, you can verify the resulting links by using JsonPath expressions, as the following example shows:

Java
mockMvc.perform(get("/people").accept(MediaType.APPLICATION_JSON))
	.andExpect(jsonPath("$.links[?(@.rel == 'self')].href").value("http://localhost:8080/people"));
Kotlin
mockMvc.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, you can verify the resulting links by using XPath expressions:

Java
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"));
Kotlin
val ns = mapOf("ns" to "http://www.w3.org/2005/Atom")
mockMvc.get("/handle") {
	accept(MediaType.APPLICATION_XML)
}.andExpect {
	xpath("/person/ns:link[@rel='self']/@href", ns) {
		string("http://localhost:8080/people")
	}
}
Async Requests

Servlet 3.0 asynchronous requests, supported in Spring MVC, work by exiting the Servlet container thread and allowing the application to compute the response asynchronously, after which an async dispatch is made to complete processing on a Servlet container thread.

In Spring MVC Test, async requests can be tested by asserting the produced async value first, then manually performing the async dispatch, and finally verifying the response. Below is an example test for controller methods that return DeferredResult, Callable, or reactive type such as Reactor Mono:

Java
@Test
void test() throws Exception {
       MvcResult mvcResult = this.mockMvc.perform(get("/path"))
               .andExpect(status().isOk()) (1)
               .andExpect(request().asyncStarted()) (2)
               .andExpect(request().asyncResult("body")) (3)
               .andReturn();

       this.mockMvc.perform(asyncDispatch(mvcResult)) (4)
               .andExpect(status().isOk()) (5)
               .andExpect(content().string("body"));
   }
1 Check response status is still unchanged
2 Async processing must have started
3 Wait and assert the async result
4 Manually perform an ASYNC dispatch (as there is no running container)
5 Verify the final response
Kotlin
@Test
fun test() {
	var mvcResult = mockMvc.get("/path").andExpect {
		status().isOk() (1)
		request { asyncStarted() } (2)
		// TODO Remove unused generic parameter
		request { asyncResult<Nothing>("body") } (3)
	}.andReturn()


	mockMvc.perform(asyncDispatch(mvcResult)) (4)
			.andExpect {
				status().isOk() (5)
				content().string("body")
			}
}
1 Check response status is still unchanged
2 Async processing must have started
3 Wait and assert the async result
4 Manually perform an ASYNC dispatch (as there is no running container)
5 Verify the final response
Streaming Responses

There are no options built into Spring MVC Test for container-less testing of streaming responses. Applications that make use of Spring MVC streaming options can use the WebTestClient to perform end-to-end, integration tests against a running server. This is also supported in Spring Boot where you can test a running server with WebTestClient. One extra advantage is the ability to use the StepVerifier from project Reactor that allows declaring expectations on a stream of data.

Filter Registrations

When setting up a MockMvc instance, you can register one or more Servlet Filter instances, as the following example shows:

Java
mockMvc = standaloneSetup(new PersonController()).addFilters(new CharacterEncodingFilter()).build();
Kotlin
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed

Registered filters are invoked through the MockFilterChain from spring-test, and the last filter delegates to the DispatcherServlet.

Spring MVC Test vs End-to-End Tests

Spring MVC Test is built on Servlet API mock implementations from the spring-test module and does not rely on a running container. Therefore, there are some differences when compared to full end-to-end integration tests with an actual client and a live server running.

The easiest way to think about this is by starting with a blank MockHttpServletRequest. Whatever you add to it is what the request becomes. Things that may catch you by surprise are that there is no context path by default; no jsessionid cookie; no forwarding, error, or async dispatches; and, therefore, no actual JSP rendering. Instead, “forwarded” and “redirected” URLs are saved in the MockHttpServletResponse and can be asserted with expectations.

This means that, if you use JSPs, you can verify the JSP page to which the request was forwarded, but no HTML is rendered. In other words, the JSP is not invoked. Note, however, that all other rendering technologies that do not rely on forwarding, such as Thymeleaf and Freemarker, render HTML to the response body as expected. The same is true for rendering JSON, XML, and other formats through @ResponseBody methods.

Alternatively, you may consider the full end-to-end integration testing support from Spring Boot with @SpringBootTest. See the Spring Boot Reference Guide.

There are pros and cons for each approach. The options provided in Spring MVC Test are different stops on the scale from classic unit testing to full integration testing. To be certain, none of the options in Spring MVC Test fall under the category of classic unit testing, but they are a little closer to it. For example, you can isolate the web layer by injecting mocked services into controllers, in which case you are testing the web layer only through the DispatcherServlet but with actual Spring configuration, as you might test the data access layer in isolation from the layers above it. Also, you can use the stand-alone setup, focusing on one controller at a time and manually providing the configuration required to make it work.

Another important distinction when using Spring MVC Test is that, conceptually, such tests are the server-side, so you can check what handler was used, if an exception was handled with a HandlerExceptionResolver, what the content of the model is, what binding errors there were, and other details. That means that it is easier to write expectations, since the server is not a black box, as it is when testing it through an actual HTTP client. This is generally an advantage of classic unit testing: It is easier to write, reason about, and debug but does not replace the need for full integration tests. At the same time, it is important not to lose sight of the fact that the response is the most important thing to check. In short, there is room here for multiple styles and strategies of testing even within the same project.

Further Examples

The framework’s own tests include many sample tests intended to show how to use Spring MVC Test. You can browse these examples for further ideas. Also, the spring-mvc-showcase project has full test coverage based on Spring MVC Test.

HtmlUnit Integration

Spring provides integration between MockMvc and HtmlUnit. This simplifies performing end-to-end testing when using HTML-based views. This integration lets you:

  • Easily test HTML pages by using tools such as HtmlUnit, WebDriver, and Geb without the need to deploy to a Servlet container.

  • Test JavaScript within pages.

  • Optionally, test using mock services to speed up testing.

  • Share logic between in-container end-to-end tests and out-of-container integration tests.

MockMvc works with templating technologies that do not rely on a Servlet Container (for example, Thymeleaf, FreeMarker, and others), but it does not work with JSPs, since they rely on the Servlet container.
Why HtmlUnit Integration?

The most obvious question that comes to mind is “Why do I need this?” The answer is best found by exploring a very basic sample application. Assume you have a Spring MVC web application that supports CRUD operations on a Message object. The application also supports paging through all messages. How would you go about testing it?

With Spring MVC Test, we can easily test if we are able to create a Message, as follows:

Java
MockHttpServletRequestBuilder createMessage = post("/messages/")
		.param("summary", "Spring Rocks")
		.param("text", "In case you didn't know, Spring Rocks!");

mockMvc.perform(createMessage)
		.andExpect(status().is3xxRedirection())
		.andExpect(redirectedUrl("/messages/123"));
Kotlin
@Test
fun test() {
	mockMvc.post("/messages/") {
		param("summary", "Spring Rocks")
		param("text", "In case you didn't know, Spring Rocks!")
	}.andExpect {
		status().is3xxRedirection()
		redirectedUrl("/messages/123")
	}
}

What if we want to test the form view that lets us create the message? For example, assume our form looks like the following snippet:

<form id="messageForm" action="/messages/" method="post">
	<div class="pull-right"><a href="/messages/">Messages</a></div>

	<label for="summary">Summary</label>
	<input type="text" class="required" id="summary" name="summary" value="" />

	<label for="text">Message</label>
	<textarea id="text" name="text"></textarea>

	<div class="form-actions">
		<input type="submit" value="Create" />
	</div>
</form>

How do we ensure that our form produce the correct request to create a new message? A naive attempt might resemble the following:

Java
mockMvc.perform(get("/messages/form"))
		.andExpect(xpath("//input[@name='summary']").exists())
		.andExpect(xpath("//textarea[@name='text']").exists());
Kotlin
mockMvc.get("/messages/form").andExpect {
	xpath("//input[@name='summary']") { exists() }
	xpath("//textarea[@name='text']") { exists() }
}

This test has some obvious drawbacks. If we update our controller to use the parameter message instead of text, our form test continues to pass, even though the HTML form is out of synch with the controller. To resolve this we can combine our two tests, as follows:

Java
String summaryParamName = "summary";
String textParamName = "text";
mockMvc.perform(get("/messages/form"))
		.andExpect(xpath("//input[@name='" + summaryParamName + "']").exists())
		.andExpect(xpath("//textarea[@name='" + textParamName + "']").exists());

MockHttpServletRequestBuilder createMessage = post("/messages/")
		.param(summaryParamName, "Spring Rocks")
		.param(textParamName, "In case you didn't know, Spring Rocks!");

mockMvc.perform(createMessage)
		.andExpect(status().is3xxRedirection())
		.andExpect(redirectedUrl("/messages/123"));
Kotlin
val summaryParamName = "summary";
val textParamName = "text";
mockMvc.get("/messages/form").andExpect {
	xpath("//input[@name='$summaryParamName']") { exists() }
	xpath("//textarea[@name='$textParamName']") { exists() }
}
mockMvc.post("/messages/") {
	param(summaryParamName, "Spring Rocks")
	param(textParamName, "In case you didn't know, Spring Rocks!")
}.andExpect {
	status().is3xxRedirection()
	redirectedUrl("/messages/123")
}

This would reduce the risk of our test incorrectly passing, but there are still some problems:

  • What if we have multiple forms on our page? Admittedly, we could update our XPath expressions, but they get more complicated as we take more factors into account: Are the fields the correct type? Are the fields enabled? And so on.

  • Another issue is that we are doing double the work we would expect. We must first verify the view, and then we submit the view with the same parameters we just verified. Ideally, this could be done all at once.

  • Finally, we still cannot account for some things. For example, what if the form has JavaScript validation that we wish to test as well?

The overall problem is that testing a web page does not involve a single interaction. Instead, it is a combination of how the user interacts with a web page and how that web page interacts with other resources. For example, the result of a form view is used as the input to a user for creating a message. In addition, our form view can potentially use additional resources that impact the behavior of the page, such as JavaScript validation.

Integration Testing to the Rescue?

To resolve the issues mentioned earlier, we could perform end-to-end integration testing, but this has some drawbacks. Consider testing the view that lets us page through the messages. We might need the following tests:

  • Does our page display a notification to the user to indicate that no results are available when the messages are empty?

  • Does our page properly display a single message?

  • Does our page properly support paging?

To set up these tests, we need to ensure our database contains the proper messages. This leads to a number of additional challenges:

  • Ensuring the proper messages are in the database can be tedious. (Consider foreign key constraints.)

  • Testing can become slow, since each test would need to ensure that the database is in the correct state.

  • Since our database needs to be in a specific state, we cannot run tests in parallel.

  • Performing assertions on such items as auto-generated ids, timestamps, and others can be difficult.

These challenges do not mean that we should abandon end-to-end integration testing altogether. Instead, we can reduce the number of end-to-end integration tests by refactoring our detailed tests to use mock services that run much faster, more reliably, and without side effects. We can then implement a small number of true end-to-end integration tests that validate simple workflows to ensure that everything works together properly.

Enter HtmlUnit Integration

So how can we achieve a balance between testing the interactions of our pages and still retain good performance within our test suite? The answer is: “By integrating MockMvc with HtmlUnit.”

HtmlUnit Integration Options

You have a number of options when you want to integrate MockMvc with HtmlUnit:

  • MockMvc and HtmlUnit: Use this option if you want to use the raw HtmlUnit libraries.

  • MockMvc and WebDriver: Use this option to ease development and reuse code between integration and end-to-end testing.

  • MockMvc and Geb: Use this option if you want to use Groovy for testing, ease development, and reuse code between integration and end-to-end testing.

MockMvc and HtmlUnit

This section describes how to integrate MockMvc and HtmlUnit. Use this option if you want to use the raw HtmlUnit libraries.

MockMvc and HtmlUnit Setup

First, make sure that you have included a test dependency on net.sourceforge.htmlunit:htmlunit. In order to use HtmlUnit with Apache HttpComponents 4.5+, you need to use HtmlUnit 2.18 or higher.

We can easily create an HtmlUnit WebClient that integrates with MockMvc by using the MockMvcWebClientBuilder, as follows:

Java
WebClient webClient;

@BeforeEach
void setup(WebApplicationContext context) {
	webClient = MockMvcWebClientBuilder
			.webAppContextSetup(context)
			.build();
}
Kotlin
lateinit var webClient: WebClient

@BeforeEach
fun setup(context: WebApplicationContext) {
	webClient = MockMvcWebClientBuilder
			.webAppContextSetup(context)
			.build()
}
This is a simple example of using MockMvcWebClientBuilder. For advanced usage, see spring-mvc-test-server-htmlunit-mah-advanced-builder.

This ensures that any URL that references localhost as the server is directed to our MockMvc instance without the need for a real HTTP connection. Any other URL is requested by using a network connection, as normal. This lets us easily test the use of CDNs.

MockMvc and HtmlUnit Usage

Now we can use HtmlUnit as we normally would but without the need to deploy our application to a Servlet container. For example, we can request the view to create a message with the following:

Java
HtmlPage createMsgFormPage = webClient.getPage("http://localhost/messages/form");
Kotlin
val createMsgFormPage = webClient.getPage("http://localhost/messages/form")
The default context path is "". Alternatively, we can specify the context path, as described in spring-mvc-test-server-htmlunit-mah-advanced-builder.

Once we have a reference to the HtmlPage, we can then fill out the form and submit it to create a message, as the following example shows:

Java
HtmlForm form = createMsgFormPage.getHtmlElementById("messageForm");
HtmlTextInput summaryInput = createMsgFormPage.getHtmlElementById("summary");
summaryInput.setValueAttribute("Spring Rocks");
HtmlTextArea textInput = createMsgFormPage.getHtmlElementById("text");
textInput.setText("In case you didn't know, Spring Rocks!");
HtmlSubmitInput submit = form.getOneHtmlElementByAttribute("input", "type", "submit");
HtmlPage newMessagePage = submit.click();
Kotlin
val form = createMsgFormPage.getHtmlElementById("messageForm")
val summaryInput = createMsgFormPage.getHtmlElementById("summary")
summaryInput.setValueAttribute("Spring Rocks")
val textInput = createMsgFormPage.getHtmlElementById("text")
textInput.setText("In case you didn't know, Spring Rocks!")
val submit = form.getOneHtmlElementByAttribute("input", "type", "submit")
val newMessagePage = submit.click()

Finally, we can verify that a new message was created successfully. The following assertions use the AssertJ library:

Java
assertThat(newMessagePage.getUrl().toString()).endsWith("/messages/123");
String id = newMessagePage.getHtmlElementById("id").getTextContent();
assertThat(id).isEqualTo("123");
String summary = newMessagePage.getHtmlElementById("summary").getTextContent();
assertThat(summary).isEqualTo("Spring Rocks");
String text = newMessagePage.getHtmlElementById("text").getTextContent();
assertThat(text).isEqualTo("In case you didn't know, Spring Rocks!");
Kotlin
assertThat(newMessagePage.getUrl().toString()).endsWith("/messages/123")
val id = newMessagePage.getHtmlElementById("id").getTextContent()
assertThat(id).isEqualTo("123")
val summary = newMessagePage.getHtmlElementById("summary").getTextContent()
assertThat(summary).isEqualTo("Spring Rocks")
val text = newMessagePage.getHtmlElementById("text").getTextContent()
assertThat(text).isEqualTo("In case you didn't know, Spring Rocks!")

The preceding code improves on our MockMvc test in a number of ways. First, we no longer have to explicitly verify our form and then create a request that looks like the form. Instead, we request the form, fill it out, and submit it, thereby significantly reducing the overhead.

Another important factor is that HtmlUnit uses the Mozilla Rhino engine to evaluate JavaScript. This means that we can also test the behavior of JavaScript within our pages.

See the HtmlUnit documentation for additional information about using HtmlUnit.

Advanced MockMvcWebClientBuilder

In the examples so far, we have used MockMvcWebClientBuilder in the simplest way possible, by building a WebClient based on the WebApplicationContext loaded for us by the Spring TestContext Framework. This approach is repeated in the following example:

Java
WebClient webClient;

@BeforeEach
void setup(WebApplicationContext context) {
	webClient = MockMvcWebClientBuilder
			.webAppContextSetup(context)
			.build();
}
Kotlin
lateinit var webClient: WebClient

@BeforeEach
fun setup(context: WebApplicationContext) {
	webClient = MockMvcWebClientBuilder
			.webAppContextSetup(context)
			.build()
}

We can also specify additional configuration options, as the following example shows:

Java
WebClient webClient;

@BeforeEach
void setup() {
	webClient = MockMvcWebClientBuilder
		// demonstrates applying a MockMvcConfigurer (Spring Security)
		.webAppContextSetup(context, springSecurity())
		// for illustration only - defaults to ""
		.contextPath("")
		// By default MockMvc is used for localhost only;
		// the following will use MockMvc for example.com and example.org as well
		.useMockMvcForHosts("example.com","example.org")
		.build();
}
Kotlin
lateinit var webClient: WebClient

@BeforeEach
fun setup() {
	webClient = MockMvcWebClientBuilder
		// demonstrates applying a MockMvcConfigurer (Spring Security)
		.webAppContextSetup(context, springSecurity())
		// for illustration only - defaults to ""
		.contextPath("")
		// By default MockMvc is used for localhost only;
		// the following will use MockMvc for example.com and example.org as well
		.useMockMvcForHosts("example.com","example.org")
		.build()
}

As an alternative, we can perform the exact same setup by configuring the MockMvc instance separately and supplying it to the MockMvcWebClientBuilder, as follows:

Java
MockMvc mockMvc = MockMvcBuilders
		.webAppContextSetup(context)
		.apply(springSecurity())
		.build();

webClient = MockMvcWebClientBuilder
		.mockMvcSetup(mockMvc)
		// for illustration only - defaults to ""
		.contextPath("")
		// By default MockMvc is used for localhost only;
		// the following will use MockMvc for example.com and example.org as well
		.useMockMvcForHosts("example.com","example.org")
		.build();
Kotlin
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed

This is more verbose, but, by building the WebClient with a MockMvc instance, we have the full power of MockMvc at our fingertips.

For additional information on creating a MockMvc instance, see spring-mvc-test-server-setup-options.
MockMvc and WebDriver

In the previous sections, we have seen how to use MockMvc in conjunction with the raw HtmlUnit APIs. In this section, we use additional abstractions within the Selenium WebDriver to make things even easier.

Why WebDriver and MockMvc?

We can already use HtmlUnit and MockMvc, so why would we want to use WebDriver? The Selenium WebDriver provides a very elegant API that lets us easily organize our code. To better show how it works, we explore an example in this section.

Despite being a part of Selenium, WebDriver does not require a Selenium Server to run your tests.

Suppose we need to ensure that a message is created properly. The tests involve finding the HTML form input elements, filling them out, and making various assertions.

This approach results in numerous separate tests because we want to test error conditions as well. For example, we want to ensure that we get an error if we fill out only part of the form. If we fill out the entire form, the newly created message should be displayed afterwards.

If one of the fields were named “summary”, we might have something that resembles the following repeated in multiple places within our tests:

Java
HtmlTextInput summaryInput = currentPage.getHtmlElementById("summary");
summaryInput.setValueAttribute(summary);
Kotlin
val summaryInput = currentPage.getHtmlElementById("summary")
summaryInput.setValueAttribute(summary)

So what happens if we change the id to smmry? Doing so would force us to update all of our tests to incorporate this change. This violates the DRY principle, so we should ideally extract this code into its own method, as follows:

Java
public HtmlPage createMessage(HtmlPage currentPage, String summary, String text) {
	setSummary(currentPage, summary);
	// ...
}

public void setSummary(HtmlPage currentPage, String summary) {
	HtmlTextInput summaryInput = currentPage.getHtmlElementById("summary");
	summaryInput.setValueAttribute(summary);
}
Kotlin
fun createMessage(currentPage: HtmlPage, summary:String, text:String) :HtmlPage{
	setSummary(currentPage, summary);
	// ...
}

fun setSummary(currentPage:HtmlPage , summary: String) {
	val summaryInput = currentPage.getHtmlElementById("summary")
	summaryInput.setValueAttribute(summary)
}

Doing so ensures that we do not have to update all of our tests if we change the UI.

We might even take this a step further and place this logic within an Object that represents the HtmlPage we are currently on, as the following example shows:

Java
public class CreateMessagePage {

	final HtmlPage currentPage;

	final HtmlTextInput summaryInput;

	final HtmlSubmitInput submit;

	public CreateMessagePage(HtmlPage currentPage) {
		this.currentPage = currentPage;
		this.summaryInput = currentPage.getHtmlElementById("summary");
		this.submit = currentPage.getHtmlElementById("submit");
	}

	public <T> T createMessage(String summary, String text) throws Exception {
		setSummary(summary);

		HtmlPage result = submit.click();
		boolean error = CreateMessagePage.at(result);

		return (T) (error ? new CreateMessagePage(result) : new ViewMessagePage(result));
	}

	public void setSummary(String summary) throws Exception {
		summaryInput.setValueAttribute(summary);
	}

	public static boolean at(HtmlPage page) {
		return "Create Message".equals(page.getTitleText());
	}
}
Kotlin
	class CreateMessagePage(private val currentPage: HtmlPage) {

		val summaryInput: HtmlTextInput = currentPage.getHtmlElementById("summary")

		val submit: HtmlSubmitInput = currentPage.getHtmlElementById("submit")

		fun <T> createMessage(summary: String, text: String): T {
			setSummary(summary)

			val result = submit.click()
			val error = at(result)

			return (if (error) CreateMessagePage(result) else ViewMessagePage(result)) as T
		}

		fun setSummary(summary: String) {
			summaryInput.setValueAttribute(summary)
		}

		fun at(page: HtmlPage): Boolean {
			return "Create Message" == page.getTitleText()
		}
	}
}

Formerly, this pattern was known as the Page Object Pattern. While we can certainly do this with HtmlUnit, WebDriver provides some tools that we explore in the following sections to make this pattern much easier to implement.

MockMvc and WebDriver Setup

To use Selenium WebDriver with the Spring MVC Test framework, make sure that your project includes a test dependency on org.seleniumhq.selenium:selenium-htmlunit-driver.

We can easily create a Selenium WebDriver that integrates with MockMvc by using the MockMvcHtmlUnitDriverBuilder as the following example shows:

Java
WebDriver driver;

@BeforeEach
void setup(WebApplicationContext context) {
	driver = MockMvcHtmlUnitDriverBuilder
			.webAppContextSetup(context)
			.build();
}
Kotlin
lateinit var driver: WebDriver

@BeforeEach
fun setup(context: WebApplicationContext) {
	driver = MockMvcHtmlUnitDriverBuilder
			.webAppContextSetup(context)
			.build()
}
This is a simple example of using MockMvcHtmlUnitDriverBuilder. For more advanced usage, see spring-mvc-test-server-htmlunit-webdriver-advanced-builder.

The preceding example ensures that any URL that references localhost as the server is directed to our MockMvc instance without the need for a real HTTP connection. Any other URL is requested by using a network connection, as normal. This lets us easily test the use of CDNs.

MockMvc and WebDriver Usage

Now we can use WebDriver as we normally would but without the need to deploy our application to a Servlet container. For example, we can request the view to create a message with the following:

Java
CreateMessagePage page = CreateMessagePage.to(driver);
Kotlin
val page = CreateMessagePage.to(driver)

We can then fill out the form and submit it to create a message, as follows:

Java
ViewMessagePage viewMessagePage =
		page.createMessage(ViewMessagePage.class, expectedSummary, expectedText);
Kotlin
val viewMessagePage =
	page.createMessage(ViewMessagePage::class, expectedSummary, expectedText)

This improves on the design of our HtmlUnit test by leveraging the Page Object Pattern. As we mentioned in spring-mvc-test-server-htmlunit-webdriver-why, we can use the Page Object Pattern with HtmlUnit, but it is much easier with WebDriver. Consider the following CreateMessagePage implementation:

Java
public class CreateMessagePage
		extends AbstractPage { (1)

	(2)
	private WebElement summary;
	private WebElement text;

	(3)
	@FindBy(css = "input[type=submit]")
	private WebElement submit;

	public CreateMessagePage(WebDriver driver) {
		super(driver);
	}

	public <T> T createMessage(Class<T> resultPage, String summary, String details) {
		this.summary.sendKeys(summary);
		this.text.sendKeys(details);
		this.submit.click();
		return PageFactory.initElements(driver, resultPage);
	}

	public static CreateMessagePage to(WebDriver driver) {
		driver.get("http://localhost:9990/mail/messages/form");
		return PageFactory.initElements(driver, CreateMessagePage.class);
	}
}
1 CreateMessagePage extends the AbstractPage. We do not go over the details of AbstractPage, but, in summary, it contains common functionality for all of our pages. For example, if our application has a navigational bar, global error messages, and other features, we can place this logic in a shared location.
2 We have a member variable for each of the parts of the HTML page in which we are interested. These are of type WebElement. WebDriver’s PageFactory lets us remove a lot of code from the HtmlUnit version of CreateMessagePage by automatically resolving each WebElement. The PageFactory#initElements(WebDriver,Class<T>) method automatically resolves each WebElement by using the field name and looking it up by the id or name of the element within the HTML page.
3 We can use the @FindBy annotation to override the default lookup behavior. Our example shows how to use the @FindBy annotation to look up our submit button with a css selector (input[type=submit]).
Kotlin
class CreateMessagePage(private val driver: WebDriver) : AbstractPage(driver) { (1)

	(2)
	private lateinit var summary: WebElement
	private lateinit var text: WebElement

	(3)
	@FindBy(css = "input[type=submit]")
	private lateinit var submit: WebElement

	fun <T> createMessage(resultPage: Class<T>, summary: String, details: String): T {
		this.summary.sendKeys(summary)
		text.sendKeys(details)
		submit.click()
		return PageFactory.initElements(driver, resultPage)
	}
	companion object {
		fun to(driver: WebDriver): CreateMessagePage {
			driver.get("http://localhost:9990/mail/messages/form")
			return PageFactory.initElements(driver, CreateMessagePage::class.java)
		}
	}
}
1 CreateMessagePage extends the AbstractPage. We do not go over the details of AbstractPage, but, in summary, it contains common functionality for all of our pages. For example, if our application has a navigational bar, global error messages, and other features, we can place this logic in a shared location.
2 We have a member variable for each of the parts of the HTML page in which we are interested. These are of type WebElement. WebDriver’s PageFactory lets us remove a lot of code from the HtmlUnit version of CreateMessagePage by automatically resolving each WebElement. The PageFactory#initElements(WebDriver,Class<T>) method automatically resolves each WebElement by using the field name and looking it up by the id or name of the element within the HTML page.
3 We can use the @FindBy annotation to override the default lookup behavior. Our example shows how to use the @FindBy annotation to look up our submit button with a css selector (input[type=submit]).

Finally, we can verify that a new message was created successfully. The following assertions use the AssertJ assertion library:

Java
assertThat(viewMessagePage.getMessage()).isEqualTo(expectedMessage);
assertThat(viewMessagePage.getSuccess()).isEqualTo("Successfully created a new message");
Kotlin
assertThat(viewMessagePage.message.isEqualTo(expectedMessage)
assertThat(viewMessagePage.success.isEqualTo("Successfully created a new message")

We can see that our ViewMessagePage lets us interact with our custom domain model. For example, it exposes a method that returns a Message object:

Java
public Message getMessage() throws ParseException {
	Message message = new Message();
	message.setId(getId());
	message.setCreated(getCreated());
	message.setSummary(getSummary());
	message.setText(getText());
	return message;
}
Kotlin
fun getMessage() = Message(getId(), getCreated(), getSummary(), getText())

We can then use the rich domain objects in our assertions.

Lastly, we must not forget to close the WebDriver instance when the test is complete, as follows:

Java
@AfterEach
void destroy() {
	if (driver != null) {
		driver.close();
	}
}
Kotlin
@AfterEach
fun destroy() {
	if (driver != null) {
		driver.close()
	}
}

For additional information on using WebDriver, see the Selenium WebDriver documentation.

Advanced MockMvcHtmlUnitDriverBuilder

In the examples so far, we have used MockMvcHtmlUnitDriverBuilder in the simplest way possible, by building a WebDriver based on the WebApplicationContext loaded for us by the Spring TestContext Framework. This approach is repeated here, as follows:

Java
WebDriver driver;

@BeforeEach
void setup(WebApplicationContext context) {
	driver = MockMvcHtmlUnitDriverBuilder
			.webAppContextSetup(context)
			.build();
}
Kotlin
lateinit var driver: WebDriver

@BeforeEach
fun setup(context: WebApplicationContext) {
	driver = MockMvcHtmlUnitDriverBuilder
			.webAppContextSetup(context)
			.build()
}

We can also specify additional configuration options, as follows:

Java
WebDriver driver;

@BeforeEach
void setup() {
	driver = MockMvcHtmlUnitDriverBuilder
			// demonstrates applying a MockMvcConfigurer (Spring Security)
			.webAppContextSetup(context, springSecurity())
			// for illustration only - defaults to ""
			.contextPath("")
			// By default MockMvc is used for localhost only;
			// the following will use MockMvc for example.com and example.org as well
			.useMockMvcForHosts("example.com","example.org")
			.build();
}
Kotlin
lateinit var driver: WebDriver

@BeforeEach
fun setup() {
	driver = MockMvcHtmlUnitDriverBuilder
			// demonstrates applying a MockMvcConfigurer (Spring Security)
			.webAppContextSetup(context, springSecurity())
			// for illustration only - defaults to ""
			.contextPath("")
			// By default MockMvc is used for localhost only;
			// the following will use MockMvc for example.com and example.org as well
			.useMockMvcForHosts("example.com","example.org")
			.build()
}

As an alternative, we can perform the exact same setup by configuring the MockMvc instance separately and supplying it to the MockMvcHtmlUnitDriverBuilder, as follows:

Java
MockMvc mockMvc = MockMvcBuilders
		.webAppContextSetup(context)
		.apply(springSecurity())
		.build();

driver = MockMvcHtmlUnitDriverBuilder
		.mockMvcSetup(mockMvc)
		// for illustration only - defaults to ""
		.contextPath("")
		// By default MockMvc is used for localhost only;
		// the following will use MockMvc for example.com and example.org as well
		.useMockMvcForHosts("example.com","example.org")
		.build();
Kotlin
// Not possible in Kotlin until https://youtrack.jetbrains.com/issue/KT-22208 is fixed

This is more verbose, but, by building the WebDriver with a MockMvc instance, we have the full power of MockMvc at our fingertips.

For additional information on creating a MockMvc instance, see spring-mvc-test-server-setup-options.
MockMvc and Geb

In the previous section, we saw how to use MockMvc with WebDriver. In this section, we use Geb to make our tests even Groovy-er.

Why Geb and MockMvc?

Geb is backed by WebDriver, so it offers many of the same benefits that we get from WebDriver. However, Geb makes things even easier by taking care of some of the boilerplate code for us.

MockMvc and Geb Setup

We can easily initialize a Geb Browser with a Selenium WebDriver that uses MockMvc, as follows:

def setup() {
	browser.driver = MockMvcHtmlUnitDriverBuilder
		.webAppContextSetup(context)
		.build()
}
This is a simple example of using MockMvcHtmlUnitDriverBuilder. For more advanced usage, see spring-mvc-test-server-htmlunit-webdriver-advanced-builder.

This ensures that any URL referencing localhost as the server is directed to our MockMvc instance without the need for a real HTTP connection. Any other URL is requested by using a network connection as normal. This lets us easily test the use of CDNs.

MockMvc and Geb Usage

Now we can use Geb as we normally would but without the need to deploy our application to a Servlet container. For example, we can request the view to create a message with the following:

to CreateMessagePage

We can then fill out the form and submit it to create a message, as follows:

when:
form.summary = expectedSummary
form.text = expectedMessage
submit.click(ViewMessagePage)

Any unrecognized method calls or property accesses or references that are not found are forwarded to the current page object. This removes a lot of the boilerplate code we needed when using WebDriver directly.

As with direct WebDriver usage, this improves on the design of our HtmlUnit test by using the Page Object Pattern. As mentioned previously, we can use the Page Object Pattern with HtmlUnit and WebDriver, but it is even easier with Geb. Consider our new Groovy-based CreateMessagePage implementation:

class CreateMessagePage extends Page {
	static url = 'messages/form'
	static at = { assert title == 'Messages : Create'; true }
	static content =  {
		submit { $('input[type=submit]') }
		form { $('form') }
		errors(required:false) { $('label.error, .alert-error')?.text() }
	}
}

Our CreateMessagePage extends Page. We do not go over the details of Page, but, in summary, it contains common functionality for all of our pages. We define a URL in which this page can be found. This lets us navigate to the page, as follows:

to CreateMessagePage

We also have an at closure that determines if we are at the specified page. It should return true if we are on the correct page. This is why we can assert that we are on the correct page, as follows:

then:
at CreateMessagePage
errors.contains('This field is required.')
We use an assertion in the closure so that we can determine where things went wrong if we were at the wrong page.

Next, we create a content closure that specifies all the areas of interest within the page. We can use a jQuery-ish Navigator API to select the content in which we are interested.

Finally, we can verify that a new message was created successfully, as follows:

then:
at ViewMessagePage
success == 'Successfully created a new message'
id
date
summary == expectedSummary
message == expectedMessage

For further details on how to get the most out of Geb, see The Book of Geb user’s manual.

Client-Side REST Tests

You can use client-side tests to test code that internally uses the RestTemplate. The idea is to declare expected requests and to provide “stub” responses so that you can focus on testing the code in isolation (that is, without running a server). The following example shows how to do so:

Java
RestTemplate restTemplate = new RestTemplate();

MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
mockServer.expect(requestTo("/greeting")).andRespond(withSuccess());

// Test code that uses the above RestTemplate ...

mockServer.verify();
Kotlin
val restTemplate = RestTemplate()

val mockServer = MockRestServiceServer.bindTo(restTemplate).build()
mockServer.expect(requestTo("/greeting")).andRespond(withSuccess())

// Test code that uses the above RestTemplate ...

mockServer.verify()

In the preceding 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 request to /greeting and want to return a 200 response with text/plain content. We can define additional expected requests and stub responses as needed. When we define expected requests and stub responses, the RestTemplate can be used in client-side code as usual. At the end of testing, mockServer.verify() can be used to verify that all expectations have been satisfied.

By default, requests are expected in the order in which expectations were declared. You can set the ignoreExpectOrder option when building the server, in which case all expectations are checked (in order) to find a match for a given request. That means requests are allowed to come in any order. The following example uses ignoreExpectOrder:

Java
server = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build();
Kotlin
server = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build()

Even with unordered requests by default, each request is allowed to execute once only. The expect method provides an overloaded variant that accepts an ExpectedCount argument that specifies a count range (for example, once, manyTimes, max, min, between, and so on). The following example uses times:

Java
RestTemplate restTemplate = new RestTemplate();

MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
mockServer.expect(times(2), requestTo("/something")).andRespond(withSuccess());
mockServer.expect(times(3), requestTo("/somewhere")).andRespond(withSuccess());

// ...

mockServer.verify();
Kotlin
val restTemplate = RestTemplate()

val mockServer = MockRestServiceServer.bindTo(restTemplate).build()
mockServer.expect(times(2), requestTo("/something")).andRespond(withSuccess())
mockServer.expect(times(3), requestTo("/somewhere")).andRespond(withSuccess())

// ...

mockServer.verify()

Note that, when ignoreExpectOrder is not set (the default), and, therefore, requests are expected in order of declaration, then that order applies only to the first of any expected request. For example if "/something" is expected two times followed by "/somewhere" three times, then there should be a request to "/something" before there is a request to "/somewhere", but, aside from that subsequent "/something" and "/somewhere", requests can come at any time.

As an alternative to all of the above, the client-side test support also provides a ClientHttpRequestFactory implementation that you can configure into a RestTemplate to bind it to a MockMvc instance. That allows processing requests using actual server-side logic but without running a server. The following example shows how to do so:

Java
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
this.restTemplate = new RestTemplate(new MockMvcClientHttpRequestFactory(mockMvc));

// Test code that uses the above RestTemplate ...
Kotlin
val mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build()
restTemplate = RestTemplate(MockMvcClientHttpRequestFactory(mockMvc))

// Test code that uses the above RestTemplate ...
Static Imports

As with server-side tests, the fluent API for client-side tests requires a few static imports. Those are easy to find by searching for 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 (such IntelliJ) may not require any additional configuration. Check for 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.