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.
This lesson assumes that:
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:
- Identify the dependencies of the tested controller.
- 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.
- Create a new
MockMvcobject 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 ownMockMvcobject. 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
TaskCrudServiceobject provides CRUD operations for tasks. - The
MessageSourceobject 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:
- Create a new
StaticMessageSourceobject and store this object in a normal instance field. We will use theStaticMessageSourceclass 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). - Create a new
TaskCrudServicemock and store this mock in a normal instance field. - Create a new
MockMvcobject 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:
- Configuring the System Under Test
- Writing Unit Tests for “Normal” Controllers, Part One - Configuration
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:
- Create a new
TaskCrudServicemock and store this mock in a normal instance field. - Create a new
MockMvcobject 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
MockMvcobject in a normal instance field because this ensures that every feature method gets its own controller object that has “clean” test doubles.