How to unit test rest controller in a Spring boot application
Spring provides the @WebMvcTest
annotation for testing Spring MVC components, providing only the configuration relevant to the MVC tests and not the full auto-configuration, so that we just have what we need to test the code within the controller only. This annotation will auto configure Spring Security and MockMvc as well, so that the security configurations are included and we can mock the services and repositories we need for the testing.
@ExtendWith(SpringExtension.class)
@WebMvcTest(TopicController.class)
public class TopicControllerTest {
@Autowired
MockMvc mockMvc;
@MockBean
TopicService topicService;
@Autowired
ObjectMapper objectMapper;
}
A typical unit test on the controller will extend the SpringExtension.class
to run the test, with the @WebMvcTest
specifying the controller class to test. The MockMvc
will be autowired, and the service used by the controller will be injected via the @MockBean
annotation. As usually the rest controller will accept and output data in json format, we usually also autowire the ObjectMapper
to convert our model class to json and back.
To stub the return value of the service, we use the given
and willReturn
.
List<Topic> topicList = List.of(
new Topic("first topic"),
new Topic("second topic")
);
given(topicService.list()).willReturn(topicList);
And to call the api and test the result, we use the MockMvc
to perform the api call, and it will return the result as a ResultActions
. We can then chain the ResultActions
with the andExpect
method with ResultMatcher
to test outcome.
mockMvc.perform(get(api))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()").value(2));
The perform
method accepts a RequestBuilder
as an argument, and we have the list of common http methods available as static methods in MockMvcRequestBuilders
to use. The above example uses the get method, another example using the post method is illustrated below.
@Test
public void whenCreateTopic_thenReturn301CreatedAndTopic() throws Exception {
Topic topic = Topic.builder()
.topicId(1L)
.title("topic")
.build();
given(topicService.create(any())).willReturn(topic);
RequestBuilder request = post(api)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(topic));
mockMvc.perform(request)
.andExpect(status().isCreated())
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andExpectAll(
jsonPath("$.topicId").value("1"),
jsonPath("$.title").value("topic")
);
}
The jsonPath
ResultMatcher allows us to verify the json results using the JsonPath syntax (illustrated in the README.md).
The example above can be found in my github repo.
This is part of a series illustrating how to build a backend Spring boot application.
- Getting Started Spring Boot Application
- Deploying to Docker
- Spring Data Testing
- Testing Services
- Unit Testing of Controller
- Integration Testing
- Code quality review with Sonarqube
- Configure Spring Security CSRF for testing on Swagger
- Configure Access Management in Spring Security
- Validate inputs in Spring Boot RestController