In the realm of Spring Boot development, the Controller layer serves as the gateway to your application, handling incoming requests, and orchestrating responses. Ensuring the correctness and reliability of this crucial layer is paramount for building robust and user-friendly web applications. Welcome to our comprehensive guide, where we embark on a journey to master the art of “Spring Boot unit test for controller layer.” This guide is designed to equip you with the knowledge and tools needed to create effective unit tests for your Controller layer, making sure your application’s endpoints function seamlessly.
Unit testing in the context of the Controller layer isn’t just about verifying HTTP request-response interactions; it’s about crafting a comprehensive testing strategy that guarantees your application’s endpoints work as expected. Throughout this article Mastering Spring Boot Unit Test for Controller Layer: A Comprehensive Guide, we’ll explore the intricacies of unit testing, with a special focus on the Controller layer. We’ll dive into essential tools and libraries provided by Spring Boot, share best practices, and address common challenges. By the end of this journey, you’ll have the skills and confidence to assess and enhance the reliability of your Controller layer, resulting in web applications that deliver outstanding user experiences.
Whether you’re a seasoned Spring Boot developer or just starting your journey, mastering “Spring Boot unit test for controller layer” is a pivotal step in building high-quality web applications. These tests not only validate the correctness of your endpoints but also contribute to a robust and maintainable codebase. So, if you’re ready to elevate your Spring Boot development skills and ensure the excellence of your Controller layer, let’s embark on this comprehensive exploration together.
This is the third part of the tutorial where we will learn about testing the Controller layer. If you miss Part 1 and Part 2, then you can check the previous post here. In the next article, we will learn about writing the JUnit test cases for Integration testing. So by the end, you will have complete knowledge of Integration testing with Spring Boot.
Read More:
- Check the Complete JUnit 5 Tutorial
- Check the Complete JavaServer Faces (JSF) Tutorial
- Check the Spring Boot JdbcTemplate Tutorials
- Check the Complete Spring Boot and Data JPA Tutorials
- Check the Complete Spring MVC Tutorials
- Check the Complete JSP Tutorials
- Check the Complete Spring Boot Tutorials [100+ Examples]
- Check the Complete Spring Boot and Thymeleaf Tutorial
- Check the Complete AWS Tutorial
- Check the Complete JavaServer Faces (JSF) Tutorial
- Check the Complete Spring Data JPA Tutorial
- Check the Complete Spring Security Tutorial
- Check the Javascript Projects for Beginners
Watch the Video on Spring Boot Unit Test for Controller Layer
Create Controller Layer
Let’s create a class MovieController.java
inside in.bushansirgur.springbootjunit.service
and add the following content
package in.bushansirgur.springbootjunit.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import in.bushansirgur.springbootjunit.model.Movie;
import in.bushansirgur.springbootjunit.service.MovieService;
@RestController
@RequestMapping("/movies")
public class MovieController {
@Autowired
private MovieService movieService;
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Movie create(@RequestBody Movie movie) {
return movieService.save(movie);
}
@GetMapping
@ResponseStatus(HttpStatus.OK)
public List<Movie> read() {
return movieService.getAllMovies();
}
@GetMapping("/{id}")
@ResponseStatus(HttpStatus.OK)
public Movie read(@PathVariable Long id) {
return movieService.getMovieById(id);
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable Long id) {
movieService.deleteMovie(id);
}
@ResponseStatus(HttpStatus.OK)
@PutMapping("/{id}")
public Movie update(@PathVariable Long id, @RequestBody Movie movie) {
return movieService.updateMovie(movie, id);
}
}
This is just a simple Spring Boot Controller class that has some REST endpoints to perform the database operations such as Create, Read, Update and Delete. We added the @RestController
annotation to make this class return the response body. We also added the @RequestMapping
annotation to provide the URI at the class level.
Create JUnit Class
Now we need to write JUnit test cases for the above controller class. In order to test the Controller layer, we will also use Mockito
. It is a testing framework, it will help us to mock the objects and we can stub the methods. It means that we will not test the real implementation, instead, we will provide a fake implementation. Let’s create MovieControllerTest.class
inside src/test/java
and add the following content
package in.bushansirgur.springbootjunit.controller;
import static org.hamcrest.CoreMatchers.is;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.time.LocalDate;
import java.time.Month;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import com.fasterxml.jackson.databind.ObjectMapper;
import in.bushansirgur.springbootjunit.model.Movie;
import in.bushansirgur.springbootjunit.service.MovieService;
@WebMvcTest
public class MovieControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private MovieService movieService;
@Autowired
private ObjectMapper objectMapper;
private Movie avatarMovie;
private Movie titanicMovie;
@BeforeEach
void init() {
avatarMovie = new Movie();
avatarMovie.setId(1L);
avatarMovie.setName("Avatar");
avatarMovie.setGenera("Action");
avatarMovie.setReleaseDate(LocalDate.of(1999, Month.APRIL, 22));
titanicMovie = new Movie();
avatarMovie.setId(2L);
titanicMovie.setName("Titanic");
titanicMovie.setGenera("Romance");
titanicMovie.setReleaseDate(LocalDate.of(2004, Month.JANUARY, 10));
}
@Test
void shouldCreateNewMovie() throws Exception {
when(movieService.save(any(Movie.class))).thenReturn(avatarMovie);
this.mockMvc.perform(post("/movies")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(avatarMovie)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.name", is(avatarMovie.getName())))
.andExpect(jsonPath("$.genera", is(avatarMovie.getGenera())))
.andExpect(jsonPath("$.releaseDate", is(avatarMovie.getReleaseDate().toString())));
}
@Test
void shouldFetchAllMovies() throws Exception {
List<Movie> list = new ArrayList<>();
list.add(avatarMovie);
list.add(titanicMovie);
when(movieService.getAllMovies()).thenReturn(list);
this.mockMvc.perform(get("/movies"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.size()", is(list.size())));
}
@Test
void shouldFetchOneMovieById() throws Exception {
when(movieService.getMovieById(anyLong())).thenReturn(avatarMovie);
this.mockMvc.perform(get("/movies/{id}", 1L))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name", is(avatarMovie.getName())))
.andExpect(jsonPath("$.genera", is(avatarMovie.getGenera())));
}
@Test
void shouldDeleteMovie() throws Exception {
doNothing().when(movieService).deleteMovie(anyLong());
this.mockMvc.perform(delete("/movies/{id}", 1L))
.andExpect(status().isNoContent());
}
@Test
void shouldUpdateMovie() throws Exception {
when(movieService.updateMovie(any(Movie.class), anyLong())).thenReturn(avatarMovie);
this.mockMvc.perform(put("/movies/{id}", 1L)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(avatarMovie)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name", is(avatarMovie.getName())))
.andExpect(jsonPath("$.genera", is(avatarMovie.getGenera())));
}
}
MockMvc
: It is the main entry point for server-side Spring MVC test support. We will use this to make HTTP requests.ObjectMapper
: We will use this to serialize any Java Object into JSON and vice versa.@MockBean
: It is used to add the mock objects into the spring application context. This mock will replace the existing bean of the same type in the application context. If no bean is available, then a new bean will be added. This is useful in integration test cases.
Now when we run the test class, we should see the green bar
Now, let’s look at the syntax for stub a method using Mockito:
when(mock.method()).thenReturn(someValue)
Here is the order that this code will execute in:
mock.method()
when(<result of step 1>)
<result of step 2>.thenReturn
When the when
method is invoked after the invocation of method()
, it delegates to MockitoCore.when
, which calls the stub()
method of the same class. This method unpacks the ongoing stubbing from the shared MockingProgress
instance that the mocked method()
invocation wrote into, and returns it. Then thenReturn
method is then called on the OngoingStubbing
instance.
That’s it for this article, in the next article we will continue with the same application where we will write the JUnit test cases for other layers as well. Thank you so much for reading this post, if you feel this post helped you then consider sharing this with your friends, colleagues, and social media.
Keywords:
spring boot test cases example, spring boot test cases for controller, spring boot test cases using mockito, spring boot test cases with h2 database, spring boot test cases for rest api, spring boot test cases junit example, spring boot test cases best practices, spring boot test junit 5 example, spring boot test junit 5 mockito example, spring boot test junit 5 controller.