🚀 Introduction: What is @WebMvcTest
in Spring Boot?
The @WebMvcTest
annotation in Spring Boot is used for unit testing controllers in a Spring MVC application. It loads only the web layer (controllers and related components) while excluding service and repository layers.
✅ Key Features of @WebMvcTest
:
✔ Loads only Spring MVC components (controllers, @ControllerAdvice
, filters).
✔ Uses MockMvc to simulate HTTP requests.
✔ Excludes service and repository layers for lightweight tests.
✔ Supports mocking dependencies using @MockitoBean
.
📌 In this guide, you’ll learn:
✅ How to test controllers using @WebMvcTest
.
✅ How it differs from @SpringBootTest
.
✅ Best practices for writing efficient controller tests.
1️⃣ Difference Between @WebMvcTest
and @SpringBootTest
Annotation | Purpose | Components Loaded |
---|---|---|
@WebMvcTest |
Unit testing controllers | Controllers, @ControllerAdvice , filters, MockMvc |
@SpringBootTest |
Full application testing | Entire application context (controllers, services, repositories, etc.) |
📌 When to Use @WebMvcTest
?
✔ When testing only the controller layer (unit tests).
✔ When mocking services instead of loading full application context.
✔ When testing request validation, exception handling, and request mappings.
2️⃣ Basic Example: Using @WebMvcTest
to Test a Controller
📌 Example: Testing a REST Controller with @WebMvcTest
1. Controller (UserController.java
)
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping("/{id}")
public ResponseEntity<String> getUserById(@PathVariable int id) {
if (id == 1) {
return ResponseEntity.ok("User: Ramesh");
}
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body("User not found");
}
}
2. Test Class (UserControllerTest.java
)
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void testGetUserById_Success() throws Exception {
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(content().string("User: Ramesh"));
}
@Test
void testGetUserById_NotFound() throws Exception {
mockMvc.perform(get("/api/users/99"))
.andExpect(status().isNotFound())
.andExpect(content().string("User not found"));
}
}
📌 Test Output:
Test passed ✅
✅ The test verifies that the controller handles different cases correctly.
✅ Using MockMvc
, we simulate HTTP requests without starting a real server.
3️⃣ Using @MockitoBean
to Inject Mocks into Controllers
Since @WebMvcTest
does not load service or repository beans, you must use @MockitoBean
to mock service dependencies.
📌 Example: Testing Controller with a Mocked Service
1. Service Layer (UserService.java
)
@Service
public class UserService {
public String getUserById(int id) {
return id == 1 ? "Ramesh" : null;
}
}
2. Controller (UserController.java
)
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/{id}")
public ResponseEntity<String> getUserById(@PathVariable int id) {
String user = userService.getUserById(id);
if (user != null) {
return ResponseEntity.ok(user);
}
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("User not found");
}
}
3. Test Class with @MockitoBean
(UserControllerTest.java
)
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockitoBean
private UserService userService;
@Test
void testGetUserById_Success() throws Exception {
when(userService.getUserById(1)).thenReturn("Ramesh");
mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(content().string("Ramesh"));
}
@Test
void testGetUserById_NotFound() throws Exception {
when(userService.getUserById(99)).thenReturn(null);
mockMvc.perform(get("/api/users/99"))
.andExpect(status().isNotFound())
.andExpect(content().string("User not found"));
}
}
📌 Test Output:
Test passed ✅
✅ @MockitoBean
replaces the real UserService
with a mock instance.
✅ Ensures that controller tests focus only on HTTP handling, not actual service logic.
4️⃣ Testing @PostMapping
with JSON Request Body
📌 Example: Testing JSON Input with MockMvc
1. Controller (UserController.java
)
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
public ResponseEntity<String> createUser(@RequestBody User user) {
return ResponseEntity.status(HttpStatus.CREATED)
.body("User " + user.getName() + " created successfully!");
}
}
2. Test Class (UserControllerTest.java
)
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void testCreateUser() throws Exception {
String userJson = """
{
"name": "Ramesh",
"email": "ramesh@example.com"
}
""";
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(userJson))
.andExpect(status().isCreated())
.andExpect(content().string("User Ramesh created successfully!"));
}
}
📌 Test Output:
Test passed ✅
✅ Tests that JSON requests are properly handled by the controller.
✅ Ensures correct HTTP status codes (201 Created
).
5️⃣ Testing Exception Handling with @ControllerAdvice
📌 Example: Testing Custom Exception Handling
1. Exception Class (UserNotFoundException.java
)
@ResponseStatus(HttpStatus.NOT_FOUND)
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
2. Global Exception Handler (GlobalExceptionHandler.java
)
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<String> handleUserNotFound(UserNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
}
}
3. Test Case (UserControllerTest.java
)
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void testUserNotFoundException() throws Exception {
mockMvc.perform(get("/api/users/99"))
.andExpect(status().isNotFound())
.andExpect(content().string("User not found"));
}
}
✅ Verifies that exceptions are correctly handled at the global level.
🎯 Summary: Best Practices for Using @WebMvcTest
✅ Use @WebMvcTest
for unit testing controllers.
✅ Use MockMvc
to simulate HTTP requests.
✅ Use @MockitoBean
to mock service dependencies.
✅ Test JSON requests and responses properly.
✅ Ensure exception handling works as expected.
🚀 Following these best practices ensures efficient and maintainable controller tests in Spring Boot!
Comments
Post a Comment
Leave Comment