Testing Spring controllers can be interesting to test. Before MockMVC existed, the options were limited to:

  • Instantiating a copy of the controller class, injected the dependencies (possibly with mocks) and calling the methods by hand.
  • Firing up the container and making HTTP calls by hand
  • Using something like selenium to automated the HTTP calls.

There are a number of disadvantages to each of these approaches. Checking the resulting HTML can require a lot of code and can be quite brittle when the UI changes. And while the direct controller approach is fairly lightweight, it doesn’t test request mapping, or any of the automatic variable conversion code. This is where MockMVC comes in, and allows testing of a large part of the MVC framework using a fluent API. 

One important thing to note is that MockMVC only supports testing the Model and Controller parts of MVC. It does not test the view transformation. 

The process for using MockMVC is setting up a MockMVC object:

[sourcecode lang=”java”]@Transactional
@RunWith(SpringRunner.class)
@SpringBootTest()
@ActiveProfiles(“test”)
public class PushControllerTest {

@Autowired
private WebApplicationContext context;

private MockMvc mvc;

@Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.build();
}

.
.
.

}[/sourcecode]Once we have the MockMVC object, we can start building a request and then test expectations on the response.

[sourcecode lang=”java5″]@Test
public void testIndex() throws Exception {
mvc.perform(get(“/index.do”))
.andExpect(status().isOk());
}[/sourcecode]This simple test checks that a GET request to /index.do returns a 2xx response. 

Tests can get much more complicated, and additional tests can be be performed, for example checking database state.

[sourcecode lang=”java5″]@Test
public void testSubmitForm() throws Exception {

assertThat(taskManager.getAll(), hasSize(0));

mvc.perform(post(“/submit.do”)
.param(“entity”, “ASSET”)
.param(“externalId”, “12345”))
.andDo(print())
.andExpect(status().isOk())
.andExpect(model().hasNoErrors())
.andExpect(model().size(2))
.andExpect(model().attributeExists(“form”))
.andExpect(model().attributeExists(“entities”))
.andExpect(model().attribute(“entities”, hasItem(EntityName.EDITION)))
.andExpect(model().attribute(“entities”, hasSize(EntityName.values().length)));

Task task = taskManager.getAll().get(0);

assertThat(task, notNullValue());
assertThat(task.getType().getName(), is(“asset”));
assertThat(task.getUrl(), is(“http://www.example.com/dam.aspx?p=xmlgetdata&entity=asset&id=12345”));

}[/sourcecode]

Creating requests

Note: In this section, examples only show partial examples with the perform() function and not the andExpect() checks.

The perform() function takes a RequestBuilder object. org.springframework.test.web.servlet.request.MockMvcRequestBuilders has a number of static methods you can use to make this easy. In the examples, we statically import the function, so that the code reads more fluently. They are:

  • get() 
  • post()
  • put()
  • patch()
  • delete()
  • options()
  • head()
  • fileUpload()

There’s also a generic request() method if you need more control, for example if you needed a TRACE request. Each method comes in two forms: one that takes a url String, and a second that takes a url template and a varargs list of parameters. The template uses names surrounded by braces, but the names are not used and parameters are substituted based on the order in the template and value list. The following two examples are identical.

[sourcecode lang=”java5″]mvc.perform(get(“/index.do?id=1”))
mvc.perform(get(“/index.do?id={id}”), 1)[/sourcecode]

Customising the Request

The returned request builder provides a great deal of options to customise the request for the test. 

Secure requests

If your code depends on running under HTTPS, you can set the request secure flag. 

[sourcecode lang=”java5″]mvc.perform(get(“/index.do?id=1”).secure(true))[/sourcecode]

Request body

When testing PUSH or PUT requests, you often need to set the body of the request. You can use either a String or a byte array.

[sourcecode lang=”java5″]String jsonBody = “{ color: “red”,value: “#f00″}”;
mvc.perform(post(“/color/1”).content(jsonBody))[/sourcecode]

Content Type

When setting the body content, you probably want to set the content type too.
[sourcecode lang=”java5″]String jsonBody = “{ color: “red”,value: “#f00″}”;
mvc.perform(post(“/color/1”)
.content(jsonBody)
.contentType(MediaType.APPLICATION_JSON))[/sourcecode]You can either use a MediaType constant or a media type String. 
[sourcecode lang=”java5″]String jsonBody = “{ color: “red”,value: “#f00″}”;
mvc.perform(post(“/color/1”)
.content(jsonBody)
.contentType(“application/json”))[/sourcecode]

Character Encoding

Setting the character encoding is as easy as passing the encoding as a string. The default will not include a charset parameter in the Content-Type header. HTTP 1.1 defaults to ISO-8859-1 in that situation.

[sourcecode lang=”java5″]String jsonBody = “{ color: “red”,value: “#f00″}”;
mvc.perform(post(“/color/1”)
.content(jsonBody)
.characterEncoding(“utf-8”))[/sourcecode]

Accept header

You can set the content types that the request will accept by either passing a vararg of MediaType objects or Strings.

[sourcecode lang=”java5″]String jsonBody = “{ color: “red”,value: “#f00″}”;
mvc.perform(post(“/color/1”)
.content(jsonBody)
.accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML))[/sourcecode][sourcecode lang=”java5″]String jsonBody = “{ color: “red”,value: “#f00″}”;
mvc.perform(post(“/color/1”)
.content(jsonBody)
.accept(“application/json”, “application/xml”))[/sourcecode]

Cookies

You can set cookies by passing a variable number of Cookie objects to the cookie() method:

[sourcecode lang=”java5″]mvc.perform(get(“/color/1”)
.cookie(new Cookie(“name”, “value”)))[/sourcecode]

Custom Headers

Finally you can add specific headers to the request if none of the specialised methods provides the functionality you require.

You can set a header by passing a header name and its value.

[sourcecode lang=”java5″]mvc.perform(get(“/color/1”)
.header(“Cache-Control”, “no-cache, no-store”))[/sourcecode]If you need multiple headers you can pass multiple values

[sourcecode lang=”java5″]mvc.perform(get(“/color/1”)
.header(“Cache-Control”, “no-cache”, “no-store”))[/sourcecode]You can also pass a HttpHeaders object to the headers() method (notice the plural). This will add to any existing headers:

[sourcecode lang=”java5″]HttpHeaders headers = new HttpHeaders();
headers.setCacheControl(“no-cache, no-store”);
mvc.perform(get(“/color/1”)
.headers(headers))[/sourcecode]

Setting common customisations

If you have a lot of customisation that happens on every request, you can use a RequestPostProcessor to customise the request used.

[sourcecode lang=”java”]public interface RequestPostProcessor {

MockHttpServletRequest postProcessRequest(MockHttpServletRequest request);

}[/sourcecode]The function is called after the Builder has built the Request object. Your method should return the modified request. 

[sourcecode lang=”java”]public class MyRequestPostProcessor implements RequestPostProcessor {
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
request.setRemoteUser(“test”);
return request;
}
}[/sourcecode]You can then use this class in your test:
[sourcecode lang=”java5″]mvc.perform(get(“/color/1”)
.with(new MyRequestPostProcessor()))[/sourcecode]You can also use a lambda if your code is small:

[sourcecode lang=”java5″]mvc.perform(get(“/color/1”)
.with(request -> {
request.setRemoteUser(“test”);
return request;
}))[/sourcecode]

In Part 2, we’ll discuss making assertions on the response.