Spring Boot Test using Junit5 and Mockito
system 2025-10-15T12:07:34Z: You are an expert on Java and spring boot
user 2025-11-13T12:29:25Z: write a test for spring boot app that teach user junit5 and mockito
assistant 2025-11-13T12:31:45Z: I’ll create a comprehensive test example for a Spring Boot application that demonstrates JUnit 5 and Mockito concepts. Let’s build a complete example with a service, repository, and controller.
Project Structure
src/
├── main/
│ ├── java/com/example/demo/
│ │ ├── controller/UserController.java
│ │ ├── service/UserService.java
│ │ ├── repository/UserRepository.java
│ │ └── model/User.java
└── test/
└── java/com/example/demo/
├── controller/UserControllerTest.java
├── service/UserServiceTest.java
└── repository/UserRepositoryTest.java
1. Domain Model
// src/main/java/com/example/demo/model/User.java
package com.example.demo.model;
import jakarta.persistence.*;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@Column(unique = true, nullable = false)
private String email;
// Constructors, getters, setters
public User() {}
public User(String name, String email) {
this.name = name;
this.email = email;
}
// Getters and setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
2. Repository
// src/main/java/com/example/demo/repository/UserRepository.java
package com.example.demo.repository;
import com.example.demo.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
boolean existsByEmail(String email);
}
3. Service
// src/main/java/com/example/demo/service/UserService.java
package com.example.demo.service;
import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public List<User> getAllUsers() {
return userRepository.findAll();
}
public Optional<User> getUserById(Long id) {
return userRepository.findById(id);
}
public User createUser(User user) {
if (userRepository.existsByEmail(user.getEmail())) {
throw new IllegalArgumentException("Email already exists");
}
return userRepository.save(user);
}
public User updateUser(Long id, User userDetails) {
User user = userRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("User not found"));
user.setName(userDetails.getName());
user.setEmail(userDetails.getEmail());
return userRepository.save(user);
}
public void deleteUser(Long id) {
if (!userRepository.existsById(id)) {
throw new IllegalArgumentException("User not found");
}
userRepository.deleteById(id);
}
public Optional<User> getUserByEmail(String email) {
return userRepository.findByEmail(email);
}
}
4. Controller
// src/main/java/com/example/demo/controller/UserController.java
package com.example.demo.controller;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping
public List<User> getAllUsers() {
return userService.getAllUsers();
}
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
Optional<User> user = userService.getUserById(id);
return user.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
try {
User createdUser = userService.createUser(user);
return ResponseEntity.ok(createdUser);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
}
}
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
try {
User updatedUser = userService.updateUser(id, user);
return ResponseEntity.ok(updatedUser);
} catch (IllegalArgumentException e) {
return ResponseEntity.notFound().build();
}
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
try {
userService.deleteUser(id);
return ResponseEntity.ok().build();
} catch (IllegalArgumentException e) {
return ResponseEntity.notFound().build();
}
}
}
5. JUnit 5 + Mockito Tests
Service Layer Test
// src/test/java/com/example/demo/service/UserServiceTest.java
package com.example.demo.service;
import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
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 java.util.Arrays;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
private User user1;
private User user2;
@BeforeEach
void setUp() {
user1 = new User("John Doe", "john@example.com");
user1.setId(1L);
user2 = new User("Jane Smith", "jane@example.com");
user2.setId(2L);
}
@Test
@DisplayName("Should return all users")
void getAllUsers_ShouldReturnAllUsers() {
// Arrange
List<User> expectedUsers = Arrays.asList(user1, user2);
when(userRepository.findAll()).thenReturn(expectedUsers);
// Act
List<User> actualUsers = userService.getAllUsers();
// Assert
assertNotNull(actualUsers);
assertEquals(2, actualUsers.size());
assertEquals("John Doe", actualUsers.get(0).getName());
verify(userRepository, times(1)).findAll();
}
@Test
@DisplayName("Should return user when valid id is provided")
void getUserById_WithValidId_ShouldReturnUser() {
// Arrange
when(userRepository.findById(1L)).thenReturn(Optional.of(user1));
// Act
Optional<User> result = userService.getUserById(1L);
// Assert
assertTrue(result.isPresent());
assertEquals("John Doe", result.get().getName());
verify(userRepository, times(1)).findById(1L);
}
@Test
@DisplayName("Should return empty when user not found")
void getUserById_WithInvalidId_ShouldReturnEmpty() {
// Arrange
when(userRepository.findById(99L)).thenReturn(Optional.empty());
// Act
Optional<User> result = userService.getUserById(99L);
// Assert
assertFalse(result.isPresent());
verify(userRepository, times(1)).findById(99L);
}
@Test
@DisplayName("Should create user when email is unique")
void createUser_WithUniqueEmail_ShouldCreateUser() {
// Arrange
User newUser = new User("New User", "new@example.com");
when(userRepository.existsByEmail("new@example.com")).thenReturn(false);
when(userRepository.save(any(User.class))).thenReturn(newUser);
// Act
User result = userService.createUser(newUser);
// Assert
assertNotNull(result);
assertEquals("New User", result.getName());
verify(userRepository, times(1)).existsByEmail("new@example.com");
verify(userRepository, times(1)).save(newUser);
}
@Test
@DisplayName("Should throw exception when email already exists")
void createUser_WithDuplicateEmail_ShouldThrowException() {
// Arrange
User duplicateUser = new User("Duplicate", "john@example.com");
when(userRepository.existsByEmail("john@example.com")).thenReturn(true);
// Act & Assert
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> userService.createUser(duplicateUser)
);
assertEquals("Email already exists", exception.getMessage());
verify(userRepository, never()).save(any(User.class));
}
@Test
@DisplayName("Should update user when user exists")
void updateUser_WithExistingUser_ShouldUpdateUser() {
// Arrange
User updatedDetails = new User("John Updated", "john.updated@example.com");
when(userRepository.findById(1L)).thenReturn(Optional.of(user1));
when(userRepository.save(any(User.class))).thenReturn(updatedDetails);
// Act
User result = userService.updateUser(1L, updatedDetails);
// Assert
assertNotNull(result);
assertEquals("John Updated", result.getName());
verify(userRepository, times(1)).findById(1L);
verify(userRepository, times(1)).save(any(User.class));
}
@Test
@DisplayName("Should throw exception when updating non-existent user")
void updateUser_WithNonExistentUser_ShouldThrowException() {
// Arrange
User updatedDetails = new User("Non Existent", "none@example.com");
when(userRepository.findById(99L)).thenReturn(Optional.empty());
// Act & Assert
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> userService.updateUser(99L, updatedDetails)
);
assertEquals("User not found", exception.getMessage());
verify(userRepository, never()).save(any(User.class));
}
@Test
@DisplayName("Should delete user when user exists")
void deleteUser_WithExistingUser_ShouldDeleteUser() {
// Arrange
when(userRepository.existsById(1L)).thenReturn(true);
doNothing().when(userRepository).deleteById(1L);
// Act
userService.deleteUser(1L);
// Assert
verify(userRepository, times(1)).existsById(1L);
verify(userRepository, times(1)).deleteById(1L);
}
@Test
@DisplayName("Should throw exception when deleting non-existent user")
void deleteUser_WithNonExistentUser_ShouldThrowException() {
// Arrange
when(userRepository.existsById(99L)).thenReturn(false);
// Act & Assert
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> userService.deleteUser(99L)
);
assertEquals("User not found", exception.getMessage());
verify(userRepository, never()).deleteById(any(Long.class));
}
}
Controller Layer Test
// src/test/java/com/example/demo/controller/UserControllerTest.java
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.BeforeEach;
import org.junit.jupiter.api.DisplayName;
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 org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.Arrays;
import java.util.Optional;
import static org.hamcrest.Matchers.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@ExtendWith(MockitoExtension.class)
class UserControllerTest {
private MockMvc mockMvc;
@Mock
private UserService userService;
@InjectMocks
private UserController userController;
private User user1;
private User user2;
private ObjectMapper objectMapper;
@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
objectMapper = new ObjectMapper();
user1 = new User("John Doe", "john@example.com");
user1.setId(1L);
user2 = new User("Jane Smith", "jane@example.com");
user2.setId(2L);
}
@Test
@DisplayName("GET /api/users - Should return all users")
void getAllUsers_ShouldReturnUsersList() throws Exception {
// Arrange
when(userService.getAllUsers()).thenReturn(Arrays.asList(user1, user2));
// Act & Assert
mockMvc.perform(get("/api/users"))
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(2)))
.andExpect(jsonPath("$[0].name", is("John Doe")))
.andExpect(jsonPath("$[1].name", is("Jane Smith")));
verify(userService, times(1)).getAllUsers();
}
@Test
@DisplayName("GET /api/users/{id} - Should return user when found")
void getUserById_WithValidId_ShouldReturnUser() throws Exception {
// Arrange
when(userService.getUserById(1L)).thenReturn(Optional.of(user1));
// Act & Assert
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id", is(1)))
.andExpect(jsonPath("$.name", is("John Doe")))
.andExpect(jsonPath("$.email", is("john@example.com")));
verify(userService, times(1)).getUserById(1L);
}
@Test
@DisplayName("GET /api/users/{id} - Should return 404 when user not found")
void getUserById_WithInvalidId_ShouldReturnNotFound() throws Exception {
// Arrange
when(userService.getUserById(99L)).thenReturn(Optional.empty());
// Act & Assert
mockMvc.perform(get("/api/users/99"))
.andExpect(status().isNotFound());
verify(userService, times(1)).getUserById(99L);
}
@Test
@DisplayName("POST /api/users - Should create user successfully")
void createUser_WithValidData_ShouldCreateUser() throws Exception {
// Arrange
User newUser = new User("New User", "new@example.com");
when(userService.createUser(any(User.class))).thenReturn(newUser);
// Act & Assert
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(newUser)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name", is("New User")))
.andExpect(jsonPath("$.email", is("new@example.com")));
verify(userService, times(1)).createUser(any(User.class));
}
@Test
@DisplayName("POST /api/users - Should return bad request for duplicate email")
void createUser_WithDuplicateEmail_ShouldReturnBadRequest() throws Exception {
// Arrange
User duplicateUser = new User("Duplicate", "john@example.com");
when(userService.createUser(any(User.class)))
.thenThrow(new IllegalArgumentException("Email already exists"));
// Act & Assert
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(duplicateUser)))
.andExpect(status().isBadRequest());
verify(userService, times(1)).createUser(any(User.class));
}
@Test
@DisplayName("PUT /api/users/{id} - Should update user successfully")
void updateUser_WithValidData_ShouldUpdateUser() throws Exception {
// Arrange
User updatedUser = new User("John Updated", "john.updated@example.com");
when(userService.updateUser(eq(1L), any(User.class))).thenReturn(updatedUser);
// Act & Assert
mockMvc.perform(put("/api/users/1")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(updatedUser)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name", is("John Updated")));
verify(userService, times(1)).updateUser(eq(1L), any(User.class));
}
@Test
@DisplayName("PUT /api/users/{id} - Should return 404 for non-existent user")
void updateUser_WithNonExistentUser_ShouldReturnNotFound() throws Exception {
// Arrange
when(userService.updateUser(eq(99L), any(User.class)))
.thenThrow(new IllegalArgumentException("User not found"));
// Act & Assert
mockMvc.perform(put("/api/users/99")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(user1)))
.andExpect(status().isNotFound());
verify(userService, times(1)).updateUser(eq(99L), any(User.class));
}
}