如何为包含WebRequest与异常处理的Spring ControllerAdvice编写JUnit测试?
Hey there! Let's walk through how to properly test your CustomizedResponseEntityExceptionHandler class. First, let's fix a couple of issues in your existing code and then build out meaningful tests step by step.
First, a Quick Fix in Your Exception Handler
Looking at your numberFormatExceptionNotFoundException method:
@ExceptionHandler(MethodArgumentTypeMismatchException.class) public final ResponseEntity<AccessError> numberFormatExceptionNotFoundException( MethodArgumentTypeMismatchException ex, NumberFormatException exe, WebRequest request) {
The NumberFormatException exe parameter is unnecessary here—since your @ExceptionHandler targets MethodArgumentTypeMismatchException, Spring won't inject a NumberFormatException into this method, which will cause it to fail when the exception is thrown. Remove that parameter to make the handler work correctly:
@ExceptionHandler(MethodArgumentTypeMismatchException.class) public final ResponseEntity<AccessError> numberFormatExceptionNotFoundException( MethodArgumentTypeMismatchException ex, WebRequest request) {
Setting Up Your Test Class
For JUnit 5, use @ExtendWith(MockitoExtension.class) to enable Mockito's mocking support. We'll mock dependencies like WebRequest and HttpServletRequest, then use @InjectMocks to wire them into your handler instance.
Here's the complete test class with tests for both of your exception handlers:
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.WebRequest; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import org.springframework.web.HttpMediaTypeNotSupportedException; import javax.servlet.http.HttpServletRequest; import java.time.OffsetDateTime; import java.util.TimeZone; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class CustomizedResponseEntityExceptionHandlerTest { @InjectMocks private CustomizedResponseEntityExceptionHandler exceptionHandler; @Mock private MethodArgumentTypeMismatchException typeMismatchEx; @Mock private ServletWebRequest servletWebRequest; @Mock private HttpServletRequest httpServletRequest; // Test for MethodArgumentTypeMismatchException handler @Test void testNumberFormatExceptionNotFoundException() { // Arrange String testPath = "/api/test"; String errorMessage = "Failed to convert value of type 'java.lang.String' to required type 'java.lang.Integer'"; when(typeMismatchEx.getMessage()).thenReturn(errorMessage); when(servletWebRequest.getRequest()).thenReturn(httpServletRequest); when(httpServletRequest.getRequestURI()).thenReturn(testPath); // Act ResponseEntity<AccessError> response = exceptionHandler.numberFormatExceptionNotFoundException(typeMismatchEx, servletWebRequest); // Assert assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); AccessError errorDetails = response.getBody(); assertThat(errorDetails).isNotNull(); assertThat(errorDetails.getCode()).isEqualTo("400"); assertThat(errorDetails.getMessage()).isEqualTo(HttpStatus.BAD_REQUEST.getReasonPhrase()); assertThat(errorDetails.getPath()).isEqualTo(testPath); assertThat(errorDetails.getTimestamp()).isNotNull().isBeforeOrEqualTo(OffsetDateTime.now(TimeZone.getTimeZone("UTC").toZoneId())); assertThat(errorDetails.getErrors()).hasSize(1); assertThat(errorDetails.getErrors().get(0).getCode()).isEqualTo("400"); assertThat(errorDetails.getErrors().get(0).getMessage()).isEqualTo(errorMessage); } // Test for HttpMediaTypeNotSupportedException handler @Test void testHandleHttpMediaTypeNotSupported() { // Arrange String testPath = "/api/upload"; HttpMediaTypeNotSupportedException mediaEx = new HttpMediaTypeNotSupportedException("application/xml"); HttpHeaders headers = new HttpHeaders(); when(servletWebRequest.getRequest()).thenReturn(httpServletRequest); when(httpServletRequest.getRequestURI()).thenReturn(testPath); // Act ResponseEntity<Object> response = exceptionHandler.handleHttpMediaTypeNotSupported(mediaEx, headers, HttpStatus.UNSUPPORTED_MEDIA_TYPE, servletWebRequest); // Assert assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); AccessError errorDetails = (AccessError) response.getBody(); assertThat(errorDetails).isNotNull(); assertThat(errorDetails.getCode()).isEqualTo("400"); assertThat(errorDetails.getMessage()).isEqualTo(HttpStatus.BAD_REQUEST.getReasonPhrase()); assertThat(errorDetails.getPath()).isEqualTo(testPath); assertThat(errorDetails.getTimestamp()).isNotNull(); assertThat(errorDetails.getErrors()).hasSize(1); assertThat(errorDetails.getErrors().get(0).getCode()).isEqualTo("400"); assertThat(errorDetails.getErrors().get(0).getMessage()).isEqualTo("Media Type Not Supported Exception"); } }
Key Details in These Tests:
- Mocking Dependencies: We mock
ServletWebRequestandHttpServletRequestto control the request URI value, which your handler uses to set thepathinAccessError. - Controlled Inputs: We define specific exception messages and paths to ensure we can verify the handler correctly uses these values in its response.
- Comprehensive Assertions: We check every field in the
AccessErrorresponse body—status code, error codes, messages, timestamp, and path—to confirm the handler behaves exactly as intended. - Testing Overridden Methods: For
handleHttpMediaTypeNotSupported, we follow the same pattern: mock required parameters and validate the output matches your custom logic.
If you'd prefer a more integration-style test (simulating actual HTTP requests), you could use MockMvc to send requests that trigger these exceptions. But the unit tests above are lightweight and focused specifically on validating your handler's logic.
内容的提问来源于stack exchange,提问作者Ant3xes

