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:
- 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
Create Service/Business Layer
Let’s create a class MovieService.java inside in.bushansirgur.springbootjunit.service
and 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
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:
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.
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.
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.