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();
}
}
package com.example.demo.controller;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Arrays;
import java.util.Optional;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Autowired
private ObjectMapper objectMapper;
@Test
void getAllUsers_ReturnsUserList() throws Exception {
User user1 = new User();
user1.setId(1L);
user1.setName("John");
user1.setEmail("john@example.com");
when(userService.findAll()).thenReturn(Arrays.asList(user1));
mockMvc.perform(get("/api/users"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].name").value("John"))
.andExpect(jsonPath("$[0].email").value("john@example.com"));
}
@Test
void getUserById_WhenExists_ReturnsUser() throws Exception {
User user = new User();
user.setId(1L);
user.setName("John");
when(userService.findById(1L)).thenReturn(Optional.of(user));
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("John"));
}
@Test
void getUserById_WhenNotExists_Returns404() throws Exception {
when(userService.findById(999L)).thenReturn(Optional.empty());
mockMvc.perform(get("/api/users/999"))
.andExpect(status().isNotFound());
}
@Test
void createUser_WithValidData_ReturnsCreated() throws Exception {
User user = new User();
user.setName("John Doe");
user.setEmail("john@example.com");
User saved = new User();
saved.setId(1L);
saved.setName("John Doe");
saved.setEmail("john@example.com");
when(userService.save(any(User.class))).thenReturn(saved);
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(user)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("John Doe"));
}
@Test
void deleteUser_WhenExists_Returns204() throws Exception {
when(userService.existsById(1L)).thenReturn(true);
mockMvc.perform(delete("/api/users/1"))
.andExpect(status().isNoContent());
}
}
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.