JUnit 5 and Mockito testing strategies

David Kumar Jan 2026
2 tabs
package com.example.demo.service;

import com.example.demo.exception.ResourceNotFoundException;
import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

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.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    private User testUser;

    @BeforeEach
    void setUp() {
        testUser = new User();
        testUser.setId(1L);
        testUser.setName("John Doe");
        testUser.setEmail("john@example.com");
    }

    @Test
    void findById_WhenUserExists_ReturnsUser() {
        // Arrange
        when(userRepository.findById(1L)).thenReturn(Optional.of(testUser));

        // Act
        Optional<User> result = userService.findById(1L);

        // Assert
        assertThat(result).isPresent();
        assertThat(result.get().getName()).isEqualTo("John Doe");
        verify(userRepository, times(1)).findById(1L);
    }

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

        // Act
        Optional<User> result = userService.findById(999L);

        // Assert
        assertThat(result).isEmpty();
    }

    @Test
    void save_WhenValidUser_SavesAndReturnsUser() {
        // Arrange
        when(userRepository.save(any(User.class))).thenReturn(testUser);

        // Act
        User result = userService.save(testUser);

        // Assert
        assertThat(result).isNotNull();
        assertThat(result.getName()).isEqualTo("John Doe");

        // Verify save was called with correct argument
        ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
        verify(userRepository).save(userCaptor.capture());
        assertThat(userCaptor.getValue().getEmail()).isEqualTo("john@example.com");
    }

    @Test
    void findAll_ReturnsAllUsers() {
        // Arrange
        User user2 = new User();
        user2.setId(2L);
        user2.setName("Jane Doe");

        when(userRepository.findAll()).thenReturn(Arrays.asList(testUser, user2));

        // Act
        List<User> results = userService.findAll();

        // Assert
        assertThat(results).hasSize(2);
        assertThat(results).extracting(User::getName)
            .containsExactly("John Doe", "Jane Doe");
    }

    @Test
    void deleteById_WhenUserExists_DeletesUser() {
        // Arrange
        doNothing().when(userRepository).deleteById(1L);

        // Act
        userService.deleteById(1L);

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

    @ParameterizedTest
    @ValueSource(strings = {"john", "JOHN", "John", "JoHn"})
    void search_WithVariousCases_ReturnsResults(String query) {
        // Arrange
        when(userRepository.findByNameContainingIgnoreCase(anyString()))
            .thenReturn(Arrays.asList(testUser));

        // Act
        List<User> results = userService.search(query);

        // Assert
        assertThat(results).isNotEmpty();
    }

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

        // Act
        boolean exists = userService.existsById(1L);

        // Assert
        assertThat(exists).isTrue();
    }
}
2 files · java Explain with highlit

JUnit 5 provides a modern testing framework with @Test, @BeforeEach, @AfterEach lifecycle hooks. Mockito creates test doubles with @Mock and @InjectMocks annotations. I use when().thenReturn() to stub method responses and verify() to confirm interactions. ArgumentCaptor captures method arguments for detailed assertions. @ParameterizedTest runs tests with multiple inputs. @ExtendWith integrates Spring's test context. MockMvc tests REST controllers without starting a server. @WebMvcTest loads only web layer for fast tests. @DataJpaTest configures an in-memory database for repository tests. AssertJ provides fluent assertions. Test slices isolate components, improving test speed and focus. Proper mocking prevents flaky tests and enables true unit testing independent of external dependencies.