📌 Why Unit Testing for Controller?
✅ Ensures API endpoints return correct HTTP status codes.
✅ Verifies request validation & response structure.
✅ Isolates controllers from database dependencies.
✅ Helps catch bugs before integration testing.
🚀 Step 1: Add Required Dependencies
📌 Ensure the following dependencies are present in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
📌 Explanation
✔️ Spring Boot Test → Provides MockMvc for testing controllers.
✔️ Mockito → Used for mocking the Service Layer.
✔️ Jackson Databind → Serializes objects to JSON for request testing.
🚀 Step 2: Mock Dependencies in Controller Layer
Since we don’t want to interact with the database, we will mock the Service Layer and test only the controller logic.
📌 Create UserControllerTest.java
inside net.javaguides.usermanagement.controller
package net.javaguides.usermanagement.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import net.javaguides.usermanagement.dto.UserDto;
import net.javaguides.usermanagement.exception.ResourceNotFoundException;
import net.javaguides.usermanagement.service.UserService;
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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import static org.mockito.Mockito.*;
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;
@Mock
private UserService userService;
@InjectMocks
private UserController userController;
private UserDto userDto;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
userDto = new UserDto(
1L,
"Ravi",
"Kumar",
"ravi.kumar@example.com",
LocalDate.of(1995, 8, 15)
);
}
}
📌 Explanation
✔️ @WebMvcTest(UserController.class)
→ Loads only the UserController
without the entire application.
✔️ @Mock
→ Mocks the UserService
, preventing actual database interactions.
✔️ @InjectMocks
→ Injects the mock into UserController
.
✔️ MockMvc
→ Simulates HTTP requests to the controller.
✔️ ObjectMapper
→ Converts Java objects to JSON format for request bodies.
🚀 Step 3: Write Unit Test for POST /api/users
📌 Add the following test inside UserControllerTest.java
@Test
void shouldCreateUser() throws Exception {
when(userService.createUser(any(UserDto.class))).thenReturn(userDto);
String json = new ObjectMapper()
.registerModule(new JavaTimeModule())
.writeValueAsString(userDto);
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1L))
.andExpect(jsonPath("$.firstName").value("Ravi"))
.andExpect(jsonPath("$.email").value("ravi.kumar@example.com"));
verify(userService, times(1)).createUser(any(UserDto.class));
}
📌 Explanation
✔️ Mocks userService.createUser()
to return a new user.
✔️ Sends a POST
request with JSON data.
✔️ Verifies response status (200 OK
) and response body.
✔️ Ensures createUser()
is called exactly once.
🚀 Step 4: Write Unit Test for GET /api/users/{id}
📌 Add the following test inside UserControllerTest.java
@Test
void shouldGetUserById() throws Exception {
when(userService.getUserById(1L)).thenReturn(userDto);
mockMvc.perform(get("/api/users/1")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1L))
.andExpect(jsonPath("$.firstName").value("Ravi"))
.andExpect(jsonPath("$.email").value("ravi.kumar@example.com"));
verify(userService, times(1)).getUserById(1L);
}
📌 Explanation
✔️ Mocks userService.getUserById(1L)
to return a user.
✔️ Verifies response status (200 OK
) and JSON data.
✔️ Ensures getUserById()
is called once.
🚀 Step 5: Handle Exception for GET /api/users/{id}
📌 Add the following test inside UserControllerTest.java
@Test
void shouldReturnNotFoundWhenUserDoesNotExist() throws Exception {
when(userService.getUserById(1L)).thenThrow(new ResourceNotFoundException("User not found with id: 1"));
mockMvc.perform(get("/api/users/1")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isNotFound());
verify(userService, times(1)).getUserById(1L);
}
📌 Explanation
✔️ Mocks userService.getUserById(1L)
to throw ResourceNotFoundException
.
✔️ Verifies response status (404 Not Found
).
🚀 Step 6: Write Unit Test for GET /api/users
📌 Add the following test inside UserControllerTest.java
@Test
void shouldGetAllUsers() throws Exception {
UserDto user2 = new UserDto(
2L,
"Priya",
"Sharma",
"priya.sharma@example.com",
LocalDate.of(1990, 5, 20)
);
List<UserDto> users = Arrays.asList(userDto, user2);
when(userService.getAllUsers()).thenReturn(users);
mockMvc.perform(get("/api/users")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.size()").value(2));
verify(userService, times(1)).getAllUsers();
}
🚀 Step 7: Write Unit Test for DELETE /api/users/{id}
📌 Add the following test inside UserControllerTest.java
@Test
void shouldDeleteUser() throws Exception {
doNothing().when(userService).deleteUser(1L);
mockMvc.perform(delete("/api/users/1")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isNoContent());
verify(userService, times(1)).deleteUser(1L);
}
🚀 Running the Tests
📌 Execute all the JUnit test cases by running the UserControllerTest class.✅ All tests should pass successfully.
🎯 Summary: What We Achieved
✔️ Mocked Service Layer using Mockito.
✔️ Tested all REST API endpoints.
✔️ Verified status codes and JSON responses.
✔️ Handled exception scenarios correctly.
🚀 Now your Spring Boot Controller Layer is fully tested with JUnit 5 & Mockito! 🎯
Comments
Post a Comment
Leave Comment