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 second part of the tutorial where we will learn about testing the business/service layer. If you miss Part 1, then you can check the previous post here. In the next article, we will learn about testing the controller layer. So by the end, we will learn how to write integration test cases and end to end cases. So let’s get started –



Read More:

Watch the video


Create Service/Business Layer


Let’s create a class MovieService.java inside in.bushansirgur.springbootjunit.serviceand the following content

package in.bushansirgur.springbootjunit.service;

import java.util.List;
import java.util.Optional;

import org.springframework.stereotype.Service;

import in.bushansirgur.springbootjunit.model.Movie;
import in.bushansirgur.springbootjunit.repository.MovieRepository;
import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class MovieService {
	
	private final MovieRepository movieRepository;
	
	public Movie save(Movie movie) {
		return movieRepository.save(movie);
	}
	
	public List<Movie> getAllMovies() {
		return movieRepository.findAll();
	}
	
	public Movie getMovieById(Long id) {
		return movieRepository.findById(id).orElseThrow(() -> new RuntimeException("Movie found for the id "+id));	
	}
	
	public Movie updateMovie(Movie movie, Long id) {
		Movie existingMovie = movieRepository.findById(id).get();
		existingMovie.setGenera(movie.getGenera());
		existingMovie.setName(movie.getName());
		existingMovie.setReleaseDate(movie.getReleaseDate());
		return movieRepository.save(existingMovie);
	}
	
	public void deleteMovie(Long id) {
		Movie existingMovie = movieRepository.findById(id).get();
		movieRepository.delete(existingMovie);
		
	}
}

Create JUnit Class


Now we need to write JUnit test cases for the above service class. In order to test the Service 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 MovieServiceTest.class inside src/test/java and add the following content

package in.bushansirgur.springbootjunit.service;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.time.LocalDate;
import java.time.Month;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import in.bushansirgur.springbootjunit.model.Movie;
import in.bushansirgur.springbootjunit.repository.MovieRepository;

@ExtendWith(MockitoExtension.class)
public class MovieServiceTest {
	
	@Mock
	private MovieRepository movieRepository;
	
	@InjectMocks
	private MovieService movieService;
	
	@Test
	void save() {
		Movie avatarMovie = new Movie();
		avatarMovie.setId(1L);
		avatarMovie.setName("Avatar");
		avatarMovie.setGenera("Action");
		avatarMovie.setReleaseDate(LocalDate.of(2000, Month.APRIL, 23));
		
		when(movieRepository.save(any(Movie.class))).thenReturn(avatarMovie);
		
		Movie newMovie = movieService.save(avatarMovie);
		
		assertNotNull(newMovie);
		assertThat(newMovie.getName()).isEqualTo("Avatar");
	}
	
	@Test
	void getMovies() {
		Movie avatarMovie = new Movie();
		avatarMovie.setId(1L);
		avatarMovie.setName("Avatar");
		avatarMovie.setGenera("Action");
		avatarMovie.setReleaseDate(LocalDate.of(2000, Month.APRIL, 23));
		
		Movie titanicMovie = new Movie();
		titanicMovie.setId(2L);
		titanicMovie.setName("Titanic");
		titanicMovie.setGenera("Romance");
		titanicMovie.setReleaseDate(LocalDate.of(2004, Month.JANUARY, 10));
		
		List<Movie> list = new ArrayList<>();
		list.add(avatarMovie);
		list.add(titanicMovie);
		
		when(movieRepository.findAll()).thenReturn(list);
		
		List<Movie> movies = movieService.getAllMovies();
		
		assertEquals(2, movies.size());
		assertNotNull(movies);
	}
	
	@Test
	void getMovieById() {
		Movie avatarMovie = new Movie();
		avatarMovie.setId(1L);
		avatarMovie.setName("Avatar");
		avatarMovie.setGenera("Action");
		avatarMovie.setReleaseDate(LocalDate.of(2000, Month.APRIL, 23));
		
		when(movieRepository.findById(anyLong())).thenReturn(Optional.of(avatarMovie));
		Movie existingMovie = movieService.getMovieById(avatarMovie.getId());
		assertNotNull(existingMovie);
		assertThat(existingMovie.getId()).isNotEqualTo(null);
	}
	
	@Test
	void getMovieByIdForException() {
		Movie avatarMovie = new Movie();
		avatarMovie.setId(1L);
		avatarMovie.setName("Avatar");
		avatarMovie.setGenera("Action");
		avatarMovie.setReleaseDate(LocalDate.of(2000, Month.APRIL, 23));
		
		when(movieRepository.findById(2L)).thenReturn(Optional.of(avatarMovie));
		assertThrows(RuntimeException.class, () -> {
			movieService.getMovieById(avatarMovie.getId());
		});
	}
	
	@Test
	void updateMovie() {
		Movie avatarMovie = new Movie();
		avatarMovie.setId(1L);
		avatarMovie.setName("Avatar");
		avatarMovie.setGenera("Action");
		avatarMovie.setReleaseDate(LocalDate.of(2000, Month.APRIL, 23));
		
		when(movieRepository.findById(anyLong())).thenReturn(Optional.of(avatarMovie));
		
		when(movieRepository.save(any(Movie.class))).thenReturn(avatarMovie);
		avatarMovie.setGenera("Fantacy");
		Movie exisitingMovie = movieService.updateMovie(avatarMovie, avatarMovie.getId());
		
		assertNotNull(exisitingMovie);
		assertEquals("Fantacy", avatarMovie.getGenera());
	}
	
	@Test
	void deleteMovie() {
		Movie avatarMovie = new Movie();
		avatarMovie.setId(1L);
		avatarMovie.setName("Avatar");
		avatarMovie.setGenera("Action");
		avatarMovie.setReleaseDate(LocalDate.of(2000, Month.APRIL, 23));
		Long movieId = 1L;
		when(movieRepository.findById(anyLong())).thenReturn(Optional.of(avatarMovie));
		doNothing().when(movieRepository).delete(any(Movie.class));
		
		movieService.deleteMovie(movieId);
		
		verify(movieRepository, times(1)).delete(avatarMovie);
	}
}

Now when we run the test class, we should see the green bar

Screenshot-2022-08-29-at-11-46-01-AM
Let’s discuss the few annotations that we used above –




  • @Mock marks the fields as mock objects. At the core, it is used to mock the objects that helps in minimizing the repetitive mock objects.
  • @InjectMocks injects mock objects to the marked fields, but the marked fields are not mocks.

Now, let’s look at the syntax for stub a method:

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.

Now let’s refactor the above test class by using JUnit life cycle method so that each time when we execute a test case, instead of setting up the data inside each test case, we can setup outside by using @BeforeEach annotation. This will execute the method every time before executing each test case.

package in.bushansirgur.springbootjunit.service;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.time.LocalDate;
import java.time.Month;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import in.bushansirgur.springbootjunit.model.Movie;
import in.bushansirgur.springbootjunit.repository.MovieRepository;

@ExtendWith(MockitoExtension.class)
public class MovieServiceTest {
	
	@Mock
	private MovieRepository movieRepository;
	
	@InjectMocks
	private MovieService movieService;
	
	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(2000, Month.APRIL, 23));
		
		titanicMovie = new Movie();
		titanicMovie.setId(2L);
		titanicMovie.setName("Titanic");
		titanicMovie.setGenera("Romance");
		titanicMovie.setReleaseDate(LocalDate.of(2004, Month.JANUARY, 10));
	}
	
	@Test
	void save() {
		
		when(movieRepository.save(any(Movie.class))).thenReturn(avatarMovie);
		
		Movie newMovie = movieService.save(avatarMovie);
		
		assertNotNull(newMovie);
		assertThat(newMovie.getName()).isEqualTo("Avatar");
	}
	
	@Test
	void getMovies() {
		
		List<Movie> list = new ArrayList<>();
		list.add(avatarMovie);
		list.add(titanicMovie);
		
		when(movieRepository.findAll()).thenReturn(list);
		
		List<Movie> movies = movieService.getAllMovies();
		
		assertEquals(2, movies.size());
		assertNotNull(movies);
	}
	
	@Test
	void getMovieById() {
		
		when(movieRepository.findById(anyLong())).thenReturn(Optional.of(avatarMovie));
		Movie existingMovie = movieService.getMovieById(avatarMovie.getId());
		assertNotNull(existingMovie);
		assertThat(existingMovie.getId()).isNotEqualTo(null);
	}
	
	@Test
	void getMovieByIdForException() {
		
		when(movieRepository.findById(2L)).thenReturn(Optional.of(avatarMovie));
		assertThrows(RuntimeException.class, () -> {
			movieService.getMovieById(avatarMovie.getId());
		});
	}
	
	@Test
	void updateMovie() {
		
		when(movieRepository.findById(anyLong())).thenReturn(Optional.of(avatarMovie));
		
		when(movieRepository.save(any(Movie.class))).thenReturn(avatarMovie);
		avatarMovie.setGenera("Fantacy");
		Movie exisitingMovie = movieService.updateMovie(avatarMovie, avatarMovie.getId());
		
		assertNotNull(exisitingMovie);
		assertEquals("Fantacy", avatarMovie.getGenera());
	}
	
	@Test
	void deleteMovie() {
		
		Long movieId = 1L;
		when(movieRepository.findById(anyLong())).thenReturn(Optional.of(avatarMovie));
		doNothing().when(movieRepository).delete(any(Movie.class));
		
		movieService.deleteMovie(movieId);
		
		verify(movieRepository, times(1)).delete(avatarMovie);
		
	}
}

Now when we run this JUnit class, we should see the green bar. All the test cases should pass as expected.

Screenshot-2022-08-29-at-11-46-01-AM

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.




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 *