Writing Unit Tests for Spring MVC Controllers - Configuration

After we have finished this lesson, we understand how we can configure the Spring MVC Test framework when we are writing unit tests with Spock Framework.

Configuring the System Under Specification

When we are writing unit tests for Spring MVC controllers, we can configure the system under specification by creating a new MockMvc object that is used by the feature methods of our specification classes.

We can create a new MockMvc object by following these steps:

  1. Identify the dependencies of the tested controller.
  2. Create the required test doubles and store the created test doubles in normal instance fields. This ensures that every feature method gets its own test double objects and the configuration done in a feature method doesn’t affect other feature methods.
  3. Create a new MockMvc object by using the standalone configuration of the Spring MVC Test framework and store the created object in a normal instance field. This ensures that every feature method gets its own MockMvc object. In other words, every feature method gets its own controller object that has “clean” test doubles.

Next, we will find out how we can configure the system under specification when we are writing unit tests for a “normal” Spring MVC controller.

Writing Unit Tests for a “Normal” Controller

Let’s assume that we are writing unit tests for the TaskCrudController class. The relevant part of this class looks as follows:

 1import org.springframework.beans.factory.annotation.Autowired;
 2import org.springframework.context.MessageSource;
 3import org.springframework.stereotype.Controller;
 4
 5@Controller
 6public class TaskCrudController {
 7
 8    private final TaskCrudService crudService;
 9    private final MessageSource messageSource;
10
11    @Autowired
12    TaskCrudController(TaskCrudService crudService,
13                       MessageSource messageSource) {
14        this.crudService = crudService;
15        this.messageSource = messageSource;
16    }
17}

As we can see, this controller has two dependencies:

  • The TaskCrudService object provides CRUD operations for tasks.
  • The MessageSource object is used to fetch the feedback messages shown to the user.

After we have identified the dependencies of the tested controller class, we can configure the system under specification by following these steps:

  1. Create a new StaticMessageSource object and store this object in a normal instance field. We will use the StaticMessageSource class because it allows us to register messages programmatically, and I think that this is a lot cleaner than configuring the response of an interaction (aka stubbing a method call).
  2. Create a new TaskCrudService mock and store this mock in a normal instance field.
  3. Create a new MockMvc object by using the standalone configuration and store the created object in a normal instance field.

After we have configured the system under specification, the source code of our specification class looks as follows:

 1import org.junit.experimental.categories.Category
 2import org.springframework.context.support.StaticMessageSource
 3import org.springframework.test.web.servlet.setup.MockMvcBuilders
 4import spock.lang.Specification
 5
 6import static com.testwithspring.master.web.WebTestConfig.exceptionResolver
 7import static com.testwithspring.master.web.WebTestConfig.fixedLocaleResolver
 8import static com.testwithspring.master.web.WebTestConfig.jspViewResolver
 9
10@Category(UnitTest.class)
11class TaskCrudControllerSpec extends Specification {
12
13    def messageSource = new StaticMessageSource()
14    def service = Mock(TaskCrudService)
15    def mockMvc = MockMvcBuilders
16            .standaloneSetup(new TaskCrudController(service, messageSource))
17            .setHandlerExceptionResolvers(exceptionResolver())
18            .setLocaleResolver(fixedLocaleResolver())
19            .setViewResolvers(jspViewResolver())
20            .build()
21}

If this configuration looks strange to you, you should take a look at the following lessons:

These lessons describe why you should configure the Spring MVC Test framework by using the standalone configuration and explain how you can create a new MockMvc object when you are writing unit tests for “normal” Spring MVC controllers.

Also, these lessons identify the required components, explain why you should create these components by using an object mother class, and describe how you can create an object mother class that creates the required components.

Finally, because the WebTestConfig class is the Groovy version of the class that we created in the lesson: Writing Unit Tests for “Normal” Controllers, Part One - Configuration, I won’t describe its implementation here.

Let’s move on and find out how we can configure the system under specification when we are writing unit tests for a REST API.

Writing Unit Tests for a REST API

Let’s assume that we are writing unit tests for the TaskCrudController class. The relevant part of this class looks as follows:

 1import org.springframework.beans.factory.annotation.Autowired;
 2import org.springframework.web.bind.annotation.RequestMapping;
 3import org.springframework.web.bind.annotation.RestController;
 4
 5@RestController
 6@RequestMapping("/api/task")
 7public class TaskCrudController {
 8
 9    private final TaskCrudService crudService;
10
11    @Autowired
12    TaskCrudController(TaskCrudService crudService) {
13        this.crudService = crudService;
14    }
15}

As we can see, this controller has only one dependency. The TaskCrudService object provides CRUD operations for tasks.

After we have identified the dependencies of the tested controller class, we can configure the system under specification by following these steps:

  1. Create a new TaskCrudService mock and store this mock in a normal instance field.
  2. Create a new MockMvc object by using the standalone configuration and store the created object in a normal instance field.

After we have configured the system under specification, the source code of our specification class looks as follows:

 1import org.junit.experimental.categories.Category
 2import org.springframework.test.web.servlet.setup.MockMvcBuilders
 3import spock.lang.Specification
 4
 5import static com.testwithspring.master.web.WebTestConfig.fixedLocaleResolver
 6import static com.testwithspring.master.web.WebTestConfig.objectMapperHttpMessageConverter
 7
 8@Category(UnitTest.class)
 9class TaskCrudControllerSpec extends Specification {
10
11    def service = Mock(TaskCrudService)
12    def mockMvc = MockMvcBuilders
13            .standaloneSetup(new TaskCrudController(service))
14            .setControllerAdvice(new TaskErrorHandler())
15            .setLocaleResolver(fixedLocaleResolver())
16            .setMessageConverters(objectMapperHttpMessageConverter())
17            .build()
18}

If this configuration looks strange to you, you should take a look at the following lessons:

These lessons describe why you should configure the Spring MVC Test framework by using the standalone configuration and explain how you can create a new MockMvc object when you are writing unit tests for a REST API.

Also, these lessons identify the required components, explain why you should create these components by using an object mother class, and describe how you can create an object mother class that creates the required components.

Finally, because the WebTestConfig class is the Groovy version of the class that we created in the lesson: Writing Unit Tests for a REST API, Part One - Configuration, I won’t describe its implementation here.

We can now configure the system under specification when are writing unit tests for Spring MVC controllers. Let’s summarize what we learned from this lesson.

Summary

This lesson has taught us two things:

  • We should store the dependencies of the tested controller class in normal instance fields because this ensures that the configuration done in a feature method cannot affect other feature methods.
  • We should store the MockMvc object in a normal instance field because this ensures that every feature method gets its own controller object that has “clean” test doubles.

Get the source code from Github