如何使用MockMvc测试带有查询参数的Spring Controller?
No worries, let's break down exactly how to test your AppointmentController using MockMvc, including how to pass query parameters correctly—even for those LocalDate parameters that might seem tricky at first.
First, Set Up Your Test Class
Start by creating a test class focused on controller testing. We'll use @WebMvcTest to isolate the controller, inject MockMvc to simulate HTTP requests, and mock your AppointmentService so we don't depend on real database calls:
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.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @WebMvcTest(AppointmentController.class) class AppointmentControllerTest { @Autowired private MockMvc mockMvc; @MockBean private AppointmentService appointmentService; // Replace with your actual service type }
Test 1: /getAppointmentById (Single Query Parameter)
For this endpoint, you just need to pass the id query parameter using the .param() method in your MockMvc request. We'll test both a successful case and an error case:
@Test void getAppointmentById_Success() throws Exception { // Mock the service response Long appointmentId = 1L; AppointmentDto mockDto = new AppointmentDto(); mockDto.setId(appointmentId); // Add other fields as needed for your DTO when(appointmentService.getAppointmentById(appointmentId)).thenReturn(mockDto); // Perform the GET request with query parameter mockMvc.perform(get("/getAppointmentById") .param("id", appointmentId.toString()) // Pass the Long as a string .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.id").value(appointmentId)); // Add more jsonPath assertions for other DTO fields } @Test void getAppointmentById_InternalServerError() throws Exception { Long appointmentId = 999L; // Mock the service to throw an exception when(appointmentService.getAppointmentById(appointmentId)).thenThrow(new RuntimeException("Not found")); mockMvc.perform(get("/getAppointmentById") .param("id", appointmentId.toString())) .andExpect(status().isInternalServerError()) .andExpect(content().string("")); // Since you return null body on error }
Test 2: /getAppointmentToPatient (Multiple Parameters Including LocalDate)
This endpoint has three query parameters: patientId, startDate, and endDate. Thanks to your @DateTimeFormat(pattern = "yyyy-MM-dd") annotation, you can just pass date strings in that format—MockMvc and Spring will automatically convert them to LocalDate objects for you:
@Test void getAppointmentToPatient_Success() throws Exception { // Mock data Long patientId = 101L; LocalDate startDate = LocalDate.of(2024, 1, 1); LocalDate endDate = LocalDate.of(2024, 1, 31); List<Appointment> mockAppointments = List.of(new Appointment()); // Populate with test data when(appointmentService.getAppointmentToPatient(patientId, startDate, endDate)).thenReturn(mockAppointments); // Perform request with all three query parameters mockMvc.perform(get("/getAppointmentToPatient") .param("patientId", patientId.toString()) .param("startDate", "2024-01-01") // Match your @DateTimeFormat pattern .param("endDate", "2024-01-31") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$").isArray()) .andExpect(jsonPath("$.length()").value(mockAppointments.size())); } @Test void getAppointmentToPatient_InternalServerError() throws Exception { Long patientId = 999L; when(appointmentService.getAppointmentToPatient(patientId, LocalDate.of(2024,1,1), LocalDate.of(2024,1,31))) .thenThrow(new RuntimeException("Error fetching appointments")); mockMvc.perform(get("/getAppointmentToPatient") .param("patientId", patientId.toString()) .param("startDate", "2024-01-01") .param("endDate", "2024-01-31")) .andExpect(status().isInternalServerError()) .andExpect(content().string("")); }
Key Notes
- Passing Query Params: Use
.param("paramName", "paramValue")for each query parameter. For numeric types likeLong, just convert to a string—Spring will handle the conversion. - LocalDate Handling: Your
@DateTimeFormatannotation tells Spring how to parse the string parameter into aLocalDate, so as long as you pass the date inyyyy-MM-ddformat, it will work seamlessly. - Mocking the Service: Using
@MockBeanensures we don't call real service logic—we control exactly what the service returns, so we can test all edge cases (success, errors) without external dependencies.
内容的提问来源于stack exchange,提问作者Suraj Rathod

