Write Unit Test in Spring Boot using JUnit 5 and Mockito

Unit testing is a kind of software testing in which code is written to test a specific piece of code to ensure that it is working correctly. A unit test verifies that the code is error-free and that the code's inputs and outputs are as expected.

In this example tutorial, we will show you how to write unit test of Service class and Repository interface in a Spring Boot using JUnit 5 and Mockito.

Required Dependencies

Add the following dependencies to your Spring Boot project:

  • JUnit Jupiter Engine - This dependency is JUnit 5.
  • Mockito Core - Mockito is required to mock objects. The basic API and implementation of the Mockito library.
  • JUnit 5 Platform Runner - This Platform Runner dependency is required to run JUnit 5 Code.
For Maven

Add to the pom.xml file:



For Gradle

Add to the build.gradle file:


testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.8.2'
testImplementation group: 'org.mockito', name: 'mockito-core', version: '4.3.1'
testImplementation group: 'org.junit.platform', name: 'junit-platform-runner', version: '1.8.2'

You can find the other versions of JUnit 5 (Junit Jupiter Engine) in the JUnit Jupiter Engine Maven repository. For other versions of Mockito core, find in the Mockito Core Maven repository. For other versions of JUnit 5 Platform Runner, find in the JUnit 5 Platform Runner Maven repository.

Unit Test Class

The following is unit testing code for Service and Repository interface:

package com.unit.testing.sample.user;

import java.util.Optional;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.platform.suite.api.Suite;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import com.unit.testing.sample.exception.NotFoundException;
import com.unit.testing.sample.user.domain.User;
import com.unit.testing.sample.user.dto.UserDto;
import com.unit.testing.sample.user.repository.UserRepository;
import com.unit.testing.sample.user.service.UserService;
import com.unit.testing.sample.user.service.impl.UserServiceImpl;

@Suite
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
    
	//InjectMocks annotation marks the field where injection should be performed.
	@InjectMocks
	UserService userService = new UserServiceImpl();
	
	//Mock annotation allows to mock the field
	@Mock
	UserRepository userRepository;

	User USER_RECORD_1;
	User USER_RECORD_2;

	@BeforeEach
	void init() {
		USER_RECORD_1 = User.builder().id("aebvsdher34khso34gkdsc").firstName("Danny").lastName("Cruz")
				.email("[email protected]").phoneNumber("11222333").deleted(false).build();
		USER_RECORD_2 = User.builder().id("aebvsdher34khso34gkdsc").firstName("Peter").lastName("Parker")
				.email("[email protected]").phoneNumber("22334455").deleted(false).build();
	}

	@Test
	void shouldRegisterNewUser_IfUserNewRegistrationDataIsValid() {
		UserDto newUser = UserDto.builder().firstName("Danny").lastName("Cruz").email("[email protected]")
				.phoneNumber("11222333").build();
		Mockito.when(userRepository.save(Mockito.any(User.class))).thenReturn(USER_RECORD_1);
		UserDto savedUser = userService.register(newUser);
		Assertions.assertNotNull(savedUser);
		Assertions.assertNotNull(savedUser.getId());
		Assertions.assertEquals(savedUser.getFirstName(), "Danny");
		Assertions.assertEquals(savedUser.getLastName(), "Cruz");
		
	}
	
	@Test
	void shouldReturnUserProfile_IfUserExistsInDatabase() {
		
		Optional<User> existingUser = Optional.of(USER_RECORD_1);
		Mockito.when(userRepository.findById(Mockito.any(String.class))).thenReturn(existingUser);
		String userId = "aebvsdher34khso34gkdsc";
		UserDto userProfile = userService.getProfile(userId);
        Assertions.assertNotNull(userProfile);
		Assertions.assertEquals(userProfile.getFirstName(), "Danny");
		Assertions.assertEquals(userProfile.getLastName(), "Cruz");
	}
	
	@Test
	void shouldDeleteUser_IfUserExistsAndHasNotDeletedBeforeInDatabase() {
		Optional<User> existingUser = Optional.of(USER_RECORD_1);
		Mockito.when(userRepository.findById(Mockito.any(String.class))).thenReturn(existingUser);
		String userId = "aebvsdher34khso34gkdsc";
		userService.delete(userId);
	}

	@Test
	void shouldUpdateRegisteredUser_IfUserExistsInDatabase() {
		UserDto updateUser = UserDto.builder().id("aebvsdher34khso34gkdsc").firstName("Peter").lastName("Parker")
				.email("[email protected]").phoneNumber("22334455").build();
		Optional<User> updatedResult = Optional.of(USER_RECORD_2);
		Mockito.when(userRepository.findById(Mockito.any(String.class))).thenReturn(updatedResult);
		Mockito.when(userRepository.save(Mockito.any(User.class))).thenReturn(USER_RECORD_2);
        UserDto updatedUser = userService.update(updateUser);
        Assertions.assertNotNull(updatedUser);
		Assertions.assertEquals(updatedUser.getFirstName(), "Peter");
		Assertions.assertEquals(updatedUser.getLastName(), "Parker");
	}

	@Test
	void shouldThrowNotFoundException_IfUserIsNotFoundInDatabaseDuringUpdate() {
		UserDto registeredUser = UserDto.builder().id("bcadu8swea3m4gso34gkd4ca").firstName("Danny").lastName("Cruz")
				.email("[email protected]").phoneNumber("11222333").build();
		Optional<User> emptyResult = Optional.empty();
		Mockito.when(userRepository.findById(Mockito.any(String.class))).thenReturn(emptyResult);
		Exception exception = Assertions.assertThrows(NotFoundException.class,
				() -> userService.update(registeredUser));
		Assertions.assertEquals("User Not found", exception.getMessage());
	}

}

Service Interface

Let the following be our UserService interface with four methods:


package com.unit.testing.sample.user.service;

import com.unit.testing.sample.user.dto.UserDto;

public interface UserService {

	UserDto register(UserDto userDto);

	UserDto getProfile(String userId);

	UserDto update(UserDto userDto);

	void delete(String userId);
}

Service Class Implementation

Let the following be the implementation class for the above UserService interface:


package com.unit.testing.sample.user.service.impl;

import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.unit.testing.sample.exception.NotFoundException;
import com.unit.testing.sample.exception.ValidationException;
import com.unit.testing.sample.user.domain.User;
import com.unit.testing.sample.user.dto.UserDto;
import com.unit.testing.sample.user.repository.UserRepository;
import com.unit.testing.sample.user.service.UserService;
import com.unit.testing.sample.user.util.UserUtil;

@Service
public class UserServiceImpl implements UserService {
	private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
	
	@Autowired
	private UserRepository userRepository;

	@Override
	public UserDto register(UserDto userDto) {
		logger.info("registering user....");
		UserUtil.validateEmail(userDto.getEmail());
		UserUtil.validateFirstName(userDto.getFirstName());
		UserUtil.validateLastName(userDto.getLastName());
		User user = User.builder().build();
		user = userRepository.save(user);
		return UserDto.builder().id(user.getId()).firstName(user.getFirstName()).lastName(user.getLastName())
				.email(user.getEmail()).phoneNumber(user.getPhoneNumber()).profilePicture(user.getProfilePicture())
				.build();
	}

	@Override
	public UserDto getProfile(String userId) {
		logger.info("getting user profile....");
		if (userId == null) {
			throw new ValidationException("userId cannot be empty");
		}
		Optional<User> userOpt = userRepository.findById(userId);
		if (!userOpt.isPresent()) {
			throw new NotFoundException("User Not found");
		}
		User user = userOpt.get();
		return UserDto.builder().id(user.getId()).firstName(user.getFirstName()).lastName(user.getLastName())
				.email(user.getEmail()).phoneNumber(user.getPhoneNumber()).profilePicture(user.getProfilePicture())
				.build();
	}

	@Override
	public UserDto update(UserDto userDto) {
		logger.info("updating user....");
		if (userDto.getId() == null) {
			throw new ValidationException("userId cannot be empty");
		}
		Optional<User> userOpt = userRepository.findById(userDto.getId());
		if (!userOpt.isPresent()) {
			throw new NotFoundException("User Not found");
		}
		User user = userOpt.get();
		user = user.toBuilder().firstName(userDto.getFirstName()).lastName(userDto.getLastName())
				.email(userDto.getEmail()).phoneNumber(userDto.getPhoneNumber())
				.profilePicture(userDto.getProfilePicture()).build();
		user = userRepository.save(user);
		return UserDto.builder().id(user.getId()).firstName(user.getFirstName()).lastName(user.getLastName())
				.email(user.getEmail()).phoneNumber(user.getPhoneNumber()).profilePicture(user.getProfilePicture())
				.build();
	}

	@Override
	public void delete(String userId) {
		logger.info("deleting user....");
		if (userId == null) {
			throw new ValidationException("userId cannot be empty");
		}
		Optional<User> userOpt = userRepository.findById(userId);
		if (!userOpt.isPresent()) {
			throw new NotFoundException("User Not found");
		}
		User user = userOpt.get();
		user = user.toBuilder().deleted(true).build();
		userRepository.save(user);
	}

}