如何为Rest调用编写JUnit测试用例?求指导CustController测试
Testing a Spring REST Controller (JUnit for
CustController) Hey there! As someone new to writing JUnit tests for REST endpoints, let's walk through how to test your CustController step by step. We'll use Spring Boot Test (with MockMvc) and Mockito—the standard tools for testing Spring REST controllers without spinning up a full server.
First, Add Required Dependencies
Make sure your pom.xml (Maven) includes these dependencies for testing:
<dependencies> <!-- Spring Boot Test Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <!-- Mockito (included in spring-boot-starter-test, but explicit is okay) --> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <scope>test</scope> </dependency> </dependencies>
Key Concepts for Testing REST Controllers
MockMvc: Simulates HTTP requests to your controller, so you don't need to start a real server.@WebMvcTest: Loads only the web layer (controllers, filters, etc.) instead of the entire Spring context—fast and focused.@MockBean: Creates mock implementations of your controller's dependencies (likeCustService,MarCusService) so you can control their behavior during tests.
Full Test Class for CustController
Here's a complete test class covering all critical scenarios in your methodCall endpoint:
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.any; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @WebMvcTest(CustController.class) class CustControllerTest { @Autowired private MockMvc mockMvc; // Handles simulated HTTP requests // Mock all dependencies of CustController @MockBean private CustService custService; @MockBean private MarCusService marCustService; @MockBean private CusValidator validator; @MockBean private CMSSiteService cmsSiteService; @MockBean private Config config; // Assuming Config is a bean holding parameters // Helper XML for CustDTO (adjust based on your actual DTO structure) private static final String VALID_CUST_DTO_XML = """ <CustDTO> <group>USER</group> <!-- Add other fields required by your DTO --> </CustDTO> """; private static final String CUST_DTO_ALL_GROUP_XML = """ <CustDTO> <group>ALL</group> </CustDTO> """; @Test void methodCall_WhenGroupIsNotAll_AndValidationFails_ReturnsOkWithErrorMessage() throws Exception { // Arrange: Make validator throw a WebServicesException doThrow(new WebServicesException("Invalid data")) .when(validator).validate(any(CustDTO.class)); // Act & Assert mockMvc.perform(put("/cutdata/methodcall") .contentType(MediaType.TEXT_XML) .content(VALID_CUST_DTO_XML)) .andExpect(status().isOk()) .andExpect(content().string("Invalid data")); } @Test void methodCall_WhenSiteIsMatching_AndMarCustReturns200_ReturnsCreatedWithMessage() throws Exception { // Arrange: Mock current site to match the configured UID CMSSiteModel mockSite = mock(CMSSiteModel.class); when(mockSite.getUid()).thenReturn("test-site-uid-matching-config"); when(cmsSiteService.getCurrentSite()).thenReturn(mockSite); when(config.getParameter("****.siteuid")).thenReturn("test-site-uid"); // Mock marCustService to return success status when(marCustService.processData(any(CustDTO.class))).thenReturn("200:Data processed successfully"); // Act & Assert mockMvc.perform(put("/cutdata/methodcall") .contentType(MediaType.TEXT_XML) .content(VALID_CUST_DTO_XML)) .andExpect(status().isCreated()) .andExpect(content().string("Data processed successfully")); } @Test void methodCall_WhenSiteIsNotMatching_AndCustServiceReturns400_ReturnsBadRequest() throws Exception { // Arrange: Mock current site to NOT match the configured UID CMSSiteModel mockSite = mock(CMSSiteModel.class); when(mockSite.getUid()).thenReturn("test-site-uid-not-matching"); when(cmsSiteService.getCurrentSite()).thenReturn(mockSite); when(config.getParameter("****.siteuid")).thenReturn("test-site-uid"); // Mock custService to return error status when(custService.processData1(any(CustDTO.class))).thenReturn("400:Invalid input"); // Act & Assert mockMvc.perform(put("/cutdata/methodcall") .contentType(MediaType.TEXT_XML) .content(VALID_CUST_DTO_XML)) .andExpect(status().isBadRequest()) .andExpect(content().string("Invalid input")); } @Test void methodCall_WhenServiceThrowsException_ReturnsBadRequest() throws Exception { // Arrange: Mock site and make service throw exception CMSSiteModel mockSite = mock(CMSSiteModel.class); when(mockSite.getUid()).thenReturn("test-site-uid"); when(cmsSiteService.getCurrentSite()).thenReturn(mockSite); when(config.getParameter("****.siteuid")).thenReturn("test-site-uid"); doThrow(new RuntimeException("Unexpected error")) .when(marCustService).processData(any(CustDTO.class)); // Act & Assert mockMvc.perform(put("/cutdata/methodcall") .contentType(MediaType.TEXT_XML) .content(VALID_CUST_DTO_XML)) .andExpect(status().isBadRequest()); } @Test void methodCall_WhenGroupIsAll_SkipsValidation() throws Exception { // Arrange: Mock site and service success CMSSiteModel mockSite = mock(CMSSiteModel.class); when(mockSite.getUid()).thenReturn("test-site-uid"); when(cmsSiteService.getCurrentSite()).thenReturn(mockSite); when(config.getParameter("****.siteuid")).thenReturn("test-site-uid"); when(marCustService.processData(any(CustDTO.class))).thenReturn("200:Success"); // Act & Assert mockMvc.perform(put("/cutdata/methodcall") .contentType(MediaType.TEXT_XML) .content(CUST_DTO_ALL_GROUP_XML)) .andExpect(status().isCreated()) .andExpect(content().string("Success")); // Verify validator was NOT called (since group is ALL) verify(validator, never()).validate(any(CustDTO.class)); } }
Explanation of Key Test Scenarios
Let's break down what each test covers:
- Validation Failure: Ensures that when the validator throws an error, the controller returns an OK status with the error message.
- Matching Site & Success: Tests the path where the current site matches the configured UID, so
marCustServiceis called, and a success response is returned. - Non-Matching Site & Error: Tests the path where the site doesn't match, so
custServiceis called, and a bad request is returned for an error status. - Service Exception: Ensures that any unexpected exception from the service results in a bad request.
- Skip Validation for ALL Group: Verifies that when the group is
ALL, the validator is never invoked (as per your controller logic).
Tips for Your Tests
- XML Request Body: Adjust the XML strings to match your actual
CustDTOfields—Spring will automatically deserialize the XML to your DTO if you have JAXB or Jackson XML dependencies set up. - Mock Config: If
Configisn't a bean, you might need to useReflectionTestUtilsto set the parameter value directly in the controller. - Log Verification: If you want to verify that the logger is called on exception, you can use a mocking framework like
Mockitoto spy on the logger.
内容的提问来源于stack exchange,提问作者Panda
相关产品推荐
相关产品推荐

