JUnit测试带@HttpStatus的自定义异常及方法改造求助
Great call deciding not to test private methods directly—you’re already thinking about good unit testing practices! Testing internal implementation details leads to brittle tests that break easily when you refactor, so exposing the logic as a public method in your Service is the right approach. Let’s walk through how to set this up properly with JUnit 5 (the latest standard) and cover both Service-level and Controller-level tests.
First, refactor that private method in your Service class to be public (or package-private if you want to limit visibility slightly, but public is cleaner for explicit testability). For example:
@Service public class YourDataService { // Other core service methods... // Refactored from private to public public void validateResult(DataResult result) throws EmptyResultException { if (result == null || result.getItems().isEmpty()) { throw new EmptyResultException("No matching data found for your request"); } } }
Use JUnit Jupiter's Assertions.assertThrows() to verify your custom exception is thrown when the conditions are met. If your Service has dependencies (like a Repository), use Mockito to mock those out and isolate the logic you're testing.
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.junit.jupiter.MockitoExtension; import static org.junit.jupiter.api.Assertions.*; @ExtendWith(MockitoExtension.class) public class YourDataServiceTest { @InjectMocks private YourDataService dataService; // Mockito injects mocked dependencies automatically @Test void validateResult_WhenResultIsEmpty_ThrowsEmptyResultException() { // Arrange: Create an empty result object DataResult emptyResult = new DataResult(); // Act & Assert: Verify the exception is thrown EmptyResultException exception = assertThrows(EmptyResultException.class, () -> { dataService.validateResult(emptyResult); }); // Optional: Verify the exception message matches your expectation assertEquals("No matching data found for your request", exception.getMessage()); } @Test void validateResult_WhenResultIsValid_DoesNotThrowException() { // Arrange: Create a valid, non-empty result DataResult validResult = new DataResult(); validResult.getItems().add(new DataItem("sample-content")); // Act & Assert: Confirm no exception is thrown assertDoesNotThrow(() -> { dataService.validateResult(validResult); }); } }
Since you have a RestController that calls this Service, you should also test that the Controller handles the exception correctly (e.g., returns a 404 Not Found status code). Use Spring's MockMvc for this integration test:
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.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.status; @WebMvcTest(YourDataController.class) public class YourDataControllerTest { @Autowired private MockMvc mockMvc; @MockBean private YourDataService dataService; // Mock the Service to control its behavior @Test void getData_WhenServiceThrowsEmptyResult_ReturnsNotFound() throws Exception { // Arrange: Make the Service throw your exception when called when(dataService.fetchAndValidateData("user-123")) .thenThrow(new EmptyResultException("No data found")); // Act & Assert: Verify the Controller returns the expected HTTP status mockMvc.perform(get("/api/data/user-123")) .andExpect(status().isNotFound()); } }
- If you really don’t want to make the method fully public, you can use package-private visibility (remove the
privatemodifier, no access specifier). Just place your test class in the same package as the Service, and it will be able to call the method without needing PowerMock. - Avoid PowerMock unless absolutely necessary—it bypasses Java’s access controls and can make tests harder to maintain. Your initial instinct to refactor instead is far better for long-term test health.
内容的提问来源于stack exchange,提问作者John

