Unit Testing Service Layer in Spring Boot Application (JUnit 5 & Mockito)

In our previous tutorial, we built a Spring Boot CRUD REST API for User Management using Spring Data JPA and MySQL. Now, we will extend that tutorial by writing unit tests for the Service Layer using JUnit 5 and Mockito.

The prerequisite for this tutorial is you need to first Build RESTful Web Services Using Spring Boot, Spring Data JPA, and MySQL

📌 Why Unit Testing?

✅ Ensures that business logic works as expected.
✅ Detects issues early before integration testing.
Isolates service logic from the database and controllers.
✅ Provides fast feedback compared to full integration tests.

🚀 Step 1: Add Required Dependencies

📌 Ensure the following dependencies are present in pom.xml


<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <scope>test</scope>
</dependency>

📌 Explanation

✔️ Mockito → Used for mocking dependencies (database, services).

🚀 Step 2: Mock Dependencies in the Service Layer

Since we don't want to interact with the actual database, we will mock the repository and test the service logic independently.

📌 Create UserServiceTest.java inside net.javaguides.usermanagement.service

package net.javaguides.usermanagement.service;

import net.javaguides.usermanagement.dto.UserDto;
import net.javaguides.usermanagement.entity.User;
import net.javaguides.usermanagement.exception.ResourceNotFoundException;
import net.javaguides.usermanagement.mapper.UserMapper;
import net.javaguides.usermanagement.repository.UserRepository;
import net.javaguides.usermanagement.service.impl.UserServiceImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.*;

class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @Mock
    private UserMapper userMapper;

    @InjectMocks
    private UserServiceImpl userService;

    private User user;
    private UserDto userDto;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);

        // Initialize test data
        user = new User();
        user.setId(1L);
        user.setFirstName("Ravi");
        user.setLastName("Kumar");
        user.setEmail("ravi.kumar@example.com");
        user.setDateOfBirth(LocalDate.of(1995, 8, 15));

        userDto = new UserDto(
                1L,
                "Ravi",
                "Kumar",
                "ravi.kumar@example.com",
                LocalDate.of(1995, 8, 15)
        );
    }
}

📌 Explanation

✔️ @Mock → Mocks UserRepository and UserMapper so that we don’t interact with the real database.
✔️ @InjectMocks → Injects mocked dependencies into UserServiceImpl.
✔️ @BeforeEach → Runs before each test to initialize test data.

🚀 Step 3: Write Unit Test for createUser Method

📌 Add the following test inside UserServiceTest.java

@Test
void shouldCreateUser() {
    // Arrange
    when(userMapper.toEntity(userDto)).thenReturn(user);
    when(userRepository.save(user)).thenReturn(user);
    when(userMapper.toDto(user)).thenReturn(userDto);

    // Act
    UserDto createdUser = userService.createUser(userDto);

    // Assert
    assertThat(createdUser).isNotNull();
    assertThat(createdUser.id()).isEqualTo(1L);
    assertThat(createdUser.email()).isEqualTo("ravi.kumar@example.com");

    verify(userRepository, times(1)).save(user);
}

📌 Explanation

✔️ Mocks the save method to return the test user.
✔️ Verifies that save() was called exactly once.
✔️ Asserts that the returned user matches expected values.

🚀 Step 4: Write Unit Test for getUserById Method

📌 Add the following test inside UserServiceTest.java

@Test
void shouldGetUserById() {
    // Arrange
    when(userRepository.findById(1L)).thenReturn(Optional.of(user));
    when(userMapper.toDto(user)).thenReturn(userDto);

    // Act
    UserDto retrievedUser = userService.getUserById(1L);

    // Assert
    assertThat(retrievedUser).isNotNull();
    assertThat(retrievedUser.id()).isEqualTo(1L);

    verify(userRepository, times(1)).findById(1L);
}

📌 Explanation

✔️ Mocks findById(1L) to return test user.
✔️ Asserts that the returned user is not null.

🚀 Step 5: Write Unit Test for Exception Handling

📌 Add the following test inside UserServiceTest.java

@Test
void shouldThrowExceptionWhenUserNotFound() {
    // Arrange
    when(userRepository.findById(1L)).thenReturn(Optional.empty());

    // Act & Assert
    assertThatThrownBy(() -> userService.getUserById(1L))
            .isInstanceOf(ResourceNotFoundException.class)
            .hasMessage("User not found with id: 1");

    verify(userRepository, times(1)).findById(1L);
}

📌 Explanation

✔️ Mocks findById(1L) to return empty (user does not exist).
✔️ Asserts that an exception is thrown when the user is not found.

🚀 Step 6: Write Unit Test for getAllUsers Method

📌 Add the following test inside UserServiceTest.java

@Test
void shouldGetAllUsers() {
    // Arrange
    when(userRepository.findAll()).thenReturn(Arrays.asList(user));
    when(userMapper.toDto(user)).thenReturn(userDto);

    // Act
    List<UserDto> users = userService.getAllUsers();

    // Assert
    assertThat(users).hasSize(1);
    verify(userRepository, times(1)).findAll();
}

🚀 Step 7: Write Unit Test for updateUser Method

📌 Add the following test inside UserServiceTest.java

@Test
void shouldUpdateUser() {
    // Arrange
    when(userRepository.findById(1L)).thenReturn(Optional.of(user));
    when(userRepository.save(user)).thenReturn(user);
    when(userMapper.toDto(user)).thenReturn(userDto);

    // Act
    UserDto updatedUser = userService.updateUser(1L, userDto);

    // Assert
    assertThat(updatedUser).isNotNull();
    verify(userRepository, times(1)).save(user);
}

🚀 Step 8: Write Unit Test for deleteUser Method

📌 Add the following test inside UserServiceTest.java

@Test
void shouldDeleteUser() {
    // Arrange
    when(userRepository.existsById(1L)).thenReturn(true);

    // Act
    userService.deleteUser(1L);

    // Assert
    verify(userRepository, times(1)).deleteById(1L);
}

🚀 Running the Tests

📌 Execute all the JUnit test cases by running the UserServiceTest class.

All tests should pass successfully.

Unit Testing Service Layer in Spring Boot Application (JUnit 5 & Mockito)

🎯 Summary: What We Achieved

✔️ Mocked dependencies using Mockito.
✔️ Wrote unit tests for all service methods.
✔️ Tested exception handling scenarios.
✔️ Ensured CRUD operations work correctly.

🚀 Now your Spring Boot Service Layer is fully tested with JUnit 5 & Mockito! 🎯

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare