如何使用MockMvc为Spring Boot含if-else逻辑的代码编写测试用例
Hey there! Let's break down how to write comprehensive MockMvc tests that cover every single if-else branch in your CartService and CartController. We'll use Mockito to mock your repository dependencies so we can control their return values and trigger each code path intentionally.
First, a quick heads-up: Your CartController uses @GetMapping for /addToCart but accepts a @RequestBody—this isn't standard HTTP practice (GET requests shouldn't include a request body). I'd recommend switching this to @PostMapping instead, and our test cases will use POST requests to match that corrected setup.
Step 1: Set Up the Test Class
Create a test class with annotations to enable Spring MVC testing and mock all dependencies used by your service:
import com.fasterxml.jackson.core.JsonProcessingException; 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 static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @WebMvcTest(CartController.class) public class CartControllerTest { @Autowired private MockMvc mockMvc; // Mock all repository/service dependencies used by CartService @MockBean private LoginRepository loginRepository; @MockBean private ProductRepository productRepository; @MockBean private CartRepository cartRepository; @MockBean private EmailService emailService; // Helper to convert Cart objects to JSON for request bodies private String asJsonString(Object obj) throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); return objectMapper.writeValueAsString(obj); } }
Step 2: Test Every Code Branch
We'll write a test case for each if-else path in your CartService.cart() method:
Branch 1: Invalid Login Credentials
Triggers the outermost else block (login fails), returning 404 Not Found.
@Test void addToCart_InvalidLogin_ReturnsNotFound() throws Exception { // Arrange: Create a cart with invalid login details Cart invalidCart = new Cart("wrongUser", "wrongPass", "test@example.com", "Laptop", "999", "Great laptop"); // Mock login repo to reject the credentials when(loginRepository.existsByUserNameAndPassword(eq("wrongUser"), eq("wrongPass"))) .thenReturn(false); // Act & Assert mockMvc.perform(post("/addToCart") .contentType(MediaType.APPLICATION_JSON) .content(asJsonString(invalidCart))) .andExpect(status().isNotFound()); }
Branch 2: Valid Login But Null Product Name
Triggers the else block where productname == null, returning 400 Bad Request.
@Test void addToCart_ValidLogin_NullProductName_ReturnsBadRequest() throws Exception { // Arrange: Cart with valid login but no product name Cart cartWithNullProduct = new Cart("validUser", "validPass", "test@example.com", null, "999", "Great laptop"); // Mock login repo to accept the credentials when(loginRepository.existsByUserNameAndPassword(eq("validUser"), eq("validPass"))) .thenReturn(true); // Act & Assert mockMvc.perform(post("/addToCart") .contentType(MediaType.APPLICATION_JSON) .content(asJsonString(cartWithNullProduct))) .andExpect(status().isBadRequest()); }
Branch 3: Valid Login & Product Name, But Product Doesn't Exist
Triggers the else block where the product isn't found in ProductRepository, returning 404 Not Found with a custom message.
@Test void addToCart_ValidLogin_ProductNotFound_ReturnsNotFound() throws Exception { // Arrange: Cart with valid login and non-existent product details Cart cartWithMissingProduct = new Cart("validUser", "validPass", "test@example.com", "NonExistentLaptop", "999", "Great laptop"); // Mock login repo to accept credentials when(loginRepository.existsByUserNameAndPassword(eq("validUser"), eq("validPass"))) .thenReturn(true); // Mock product repo to report the product doesn't exist when(productRepository.existsByProductNameAndPriceAndDiscription( eq("NonExistentLaptop"), eq("999"), eq("Great laptop"))) .thenReturn(false); // Act & Assert mockMvc.perform(post("/addToCart") .contentType(MediaType.APPLICATION_JSON) .content(asJsonString(cartWithMissingProduct))) .andExpect(status().isNotFound()) .andExpect(jsonPath("$.message").value("entity not found")); }
Branch 4: All Conditions Met (Happy Path)
Triggers all if blocks, successfully adding the product to the cart and returning a success message.
@Test void addToCart_AllValid_ReturnsSuccess() throws Exception { // Arrange: Cart with valid login and existing product details Cart validCart = new Cart("validUser", "validPass", "test@example.com", "Laptop", "999", "Great laptop"); // Mock login repo to accept credentials when(loginRepository.existsByUserNameAndPassword(eq("validUser"), eq("validPass"))) .thenReturn(true); // Mock product repo to confirm the product exists when(productRepository.existsByProductNameAndPriceAndDiscription( eq("Laptop"), eq("999"), eq("Great laptop"))) .thenReturn(true); // Mock cart repo to return a count of 1 (existing items in cart) when(cartRepository.countByUserName(eq("validUser"))) .thenReturn(1); // Mock the save operation (we don't need to persist real data) doNothing().when(cartRepository).save(any(Cart.class)); // Act & Assert mockMvc.perform(post("/addToCart") .contentType(MediaType.APPLICATION_JSON) .content(asJsonString(validCart))) .andExpect(status().isOk()) .andExpect(content().string("{\"message\":\"product Successfully added to cart\"}")); // Optional: Verify the cart repo's save method was called exactly once verify(cartRepository, times(1)).save(any(Cart.class)); }
Key Notes
- Mocking Logic:
@MockBeanreplaces real repository beans with Mockito mocks, letting us control their return values to trigger specific code paths. - JSON Serialization: The
asJsonStringhelper converts yourCartobject to JSON—ensure yourCartclass has a no-arg constructor and getters/setters for Jackson to work correctly. - Validation: If you add validation annotations to your
Cartclass (like@NotNullfor required fields), you can add additional tests to verify invalid input returns400 Bad Request.
内容的提问来源于stack exchange,提问作者user14563905

