Spring Boot Unit Testing with JUnit Mockito and MockMvc

Hey guys in this article, we will learn about writing Unit test cases for Spring Boot applications using JUnit, Mockito, and MockMvc with easy to understand examples. 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.

Writing Spring Boot test cases for the Controller layer is very important in our day to day development. Most developers write Spring Boot test cases using the Mockito testing framework. We will also use Mockito and we will write Spring Boot test cases with H2 Database.



Read More:

Watch the Video


Create Controller Layer


Let’s create a class MovieController.java inside in.bushansirgur.springbootjunit.serviceand 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
Screenshot-2022-09-27-at-8-27-54-PM

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:

  1. mock.method()
  2. when(<result of step 1>)
  3. <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.



About the author

Bushan Sirgur

Hey guys, I am Bushan Sirgur from Banglore, India. Currently, I am working as an Associate project in an IT company.

View all posts

Leave a Reply

Your email address will not be published. Required fields are marked *