Writing Tests for Spring MVC Controllers: Test Case 101

The previous part of my new Spring MVC Test tutorial taught us how we can configure the system under test when we are writing unit tests for "normal" Spring MVC controllers. We also learned that if we don't want to add duplicate code to our test classes, we should send HTTP requests to the system under test by using request builder classes. However, before we can implement the request builder methods which create and send HTTP requests to the system under test, we have to understand:

  • How we can send HTTP requests by using the MockMvc class.
  • How we can write assertions for the returned HTTP response.

Unfortunately, this topic is so big that it cannot be covered in one blog post. That's why this blog post provides a very quick introduction to this topic, and the next parts of this tutorial provide "proper" answers to these questions.

Let's begin.

The Basic Structure of an Automated Test

When we want to write an automated test for a Spring MVC controller, we have to follow these steps:

  1. Create a RequestBuilder object which specifies what kind of an HTTP request is send to the system under test.
  2. Send an HTTP request to the system under test by invoking the perform() method of the MockMvc class, and pass the created RequestBuilder object as a method parameter.
  3. Write assertions for the returned HTTP response by using the ResultActions object that's returned by the perform() method of the MockMvc class. We can write an assertion for the returned HTTP response by invoking the andExpect() method of the ResultActions interface. When we invoke this method, we have to create a new ResultMatcher object and pass this object as a method parameter.

The pseudo code of our automated test looks as follows:

mockMvc.perform(
	//Specify what kind of an HTTP request is send to the system under test
)
.andExpect(
	//Write an assertion for the returned HTTP response
)
Because the andExpect() method of the ResultActions interface returns a ResultActions object, we can (and we should) perform multiple assertions by simply invoking the andExpect() method multiple times. We will talk more about writing assertions in an upcoming part of this tutorial.

Next, we will learn how we can specify what kind of an HTTP request is send to the system under test.

Specifying the HTTP request That's Send to the System Under Test

When we want to specify the HTTP request that's send to the system under test, we have to invoke a static factory method of the MockMvcRequestBuilders class. This class provides factory methods which allow us to send GET, POST, PUT, PATCH, DELETE, OPTIONS, and HEAD requests to the system under test. All factory methods return a MockHttpServletRequestBuilder object that can be passed to the the perform() method of the MockMvc class as a method parameter.

When we invoke a factory method that creates a new MockHttpServletRequestBuilder object, we have to pass two method parameters to the invoked method. These method parameters are:

  1. A URI template which uses the format that's supported by the UriComponentsBuilder class.
  2. Zero or more URI variable values. These values are used to replace the variables found from the URI template by using the FIFO (first in, first out) principle.

Let's take a look at two examples which demonstrate how we can use the factory methods of the MockMvcRequestBuilders class.

Example 1:

If we want to send a GET request to the path: '/api/task/1', we have to invoke the get() method of the MockMvcRequestBuilders class. After we have invoked this method, the pseudo code of our automated test looks as follows:

mockMvc.perform(get("/api/task/{id}", 1L))
.andExpect(
	//Write an assertion for the returned HTTP response
)

Example 2:

If we want to send a PUT request to the path: '/api/user/99/task/1', we have to invoke the put() method of the MockMvcRequestBuilders class. After we have invoked this method, the pseudo code of our automated test looks as follows:

mockMvc.perform(put("/api/user/{userId}/task/{taskId}", 99L, 1L))
.andExpect(
	//Write an assertion for the returned HTTP response
)
There are three things I want to point out:

  • We could also use a hard coded URI string instead of a URI template. For example, we could replace '/api/task/{id}' with '/api/task/1'. However, this doesn't make sense if we are using a request builder class because the odds are that it has to send HTTP requests to different URIs.
  • When we invoke a factory method that creates a new MockHttpServletRequestBuilder object, we could also create a URI object and pass that object as a method parameter. I don't personally use this approach because I think that passing a URI template and variable values as method parameters makes my code easier to read.
  • The MockHttpServletRequestBuilder class has a very versatile API which allows us to build the HTTP request that's send to the system under test. We will take a closer look at this API in the next part of this tutorial.

Additional Reading:

We can now describe the structure of an automated test which uses the Spring MVC Test framework, and we can send a simple HTTP request to the system under test. Let's summarize what we learned from this blog post.

Summary

This blog post has taught us three things:

  • We can send an HTTP request to the system under test by invoking the perform() method of the MockMvc class.
  • We can create a new a MockHttpServletRequestBuilder object, which allows us to build the HTTP request that's send to the system under test, by using the static factory methods of the MockMvcRequestBuilders class.
  • When we specify the Request-URI, we should use the syntax that's supported by the UriComponentsBuilder class.
0 comments… add one

Leave a Reply