|
20 | 20 | import java.util.Collections;
|
21 | 21 | import java.util.List;
|
22 | 22 | import java.util.Locale;
|
23 |
| -import java.util.Map; |
24 |
| -import java.util.function.BiFunction; |
25 | 23 |
|
26 | 24 | import org.junit.jupiter.api.Test;
|
27 | 25 |
|
28 | 26 | import org.springframework.beans.testfixture.beans.TestBean;
|
29 |
| -import org.springframework.context.MessageSource; |
| 27 | +import org.springframework.context.MessageSourceResolvable; |
30 | 28 | import org.springframework.context.support.StaticMessageSource;
|
31 | 29 | import org.springframework.core.MethodParameter;
|
32 | 30 | import org.springframework.http.HttpHeaders;
|
|
36 | 34 | import org.springframework.http.ProblemDetail;
|
37 | 35 | import org.springframework.lang.Nullable;
|
38 | 36 | import org.springframework.util.LinkedMultiValueMap;
|
39 |
| -import org.springframework.validation.BindException; |
| 37 | +import org.springframework.validation.BeanPropertyBindingResult; |
40 | 38 | import org.springframework.validation.BindingResult;
|
41 |
| -import org.springframework.validation.ObjectError; |
| 39 | +import org.springframework.validation.beanvalidation.MethodValidationResult; |
42 | 40 | import org.springframework.web.bind.MethodArgumentNotValidException;
|
43 | 41 | import org.springframework.web.bind.MissingMatrixVariableException;
|
44 | 42 | import org.springframework.web.bind.MissingPathVariableException;
|
|
48 | 46 | import org.springframework.web.bind.UnsatisfiedServletRequestParameterException;
|
49 | 47 | import org.springframework.web.bind.support.WebExchangeBindException;
|
50 | 48 | import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
|
| 49 | +import org.springframework.web.method.annotation.HandlerMethodValidationException; |
51 | 50 | import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
52 | 51 | import org.springframework.web.server.MethodNotAllowedException;
|
53 | 52 | import org.springframework.web.server.MissingRequestValueException;
|
|
59 | 58 | import org.springframework.web.util.BindErrorUtils;
|
60 | 59 |
|
61 | 60 | import static org.assertj.core.api.Assertions.assertThat;
|
| 61 | +import static org.mockito.BDDMockito.mock; |
| 62 | +import static org.mockito.BDDMockito.reset; |
| 63 | +import static org.mockito.BDDMockito.when; |
62 | 64 |
|
63 | 65 | /**
|
64 | 66 | * Unit tests that verify the HTTP response details exposed by exceptions in the
|
@@ -245,20 +247,35 @@ void missingServletRequestPartException() {
|
245 | 247 | @Test
|
246 | 248 | void methodArgumentNotValidException() {
|
247 | 249 |
|
248 |
| - MessageSourceTestHelper messageSourceHelper = new MessageSourceTestHelper(MethodArgumentNotValidException.class); |
249 |
| - BindingResult bindingResult = messageSourceHelper.initBindingResult(); |
| 250 | + ValidationTestHelper testHelper = new ValidationTestHelper(MethodArgumentNotValidException.class); |
| 251 | + BindingResult result = testHelper.bindingResult(); |
250 | 252 |
|
251 |
| - MethodArgumentNotValidException ex = new MethodArgumentNotValidException(this.methodParameter, bindingResult); |
| 253 | + MethodArgumentNotValidException ex = new MethodArgumentNotValidException(this.methodParameter, result); |
252 | 254 |
|
253 | 255 | assertStatus(ex, HttpStatus.BAD_REQUEST);
|
254 | 256 | assertDetail(ex, "Invalid request content.");
|
255 |
| - messageSourceHelper.assertDetailMessage(ex); |
256 |
| - messageSourceHelper.assertErrorMessages( |
257 |
| - (source, locale) -> BindErrorUtils.resolve(ex.getAllErrors(), source, locale)); |
| 257 | + testHelper.assertMessages(ex, ex.getAllErrors()); |
258 | 258 |
|
259 | 259 | assertThat(ex.getHeaders()).isEmpty();
|
260 | 260 | }
|
261 | 261 |
|
| 262 | + @Test |
| 263 | + void handlerMethodValidationException() { |
| 264 | + MethodValidationResult result = mock(MethodValidationResult.class); |
| 265 | + when(result.isForReturnValue()).thenReturn(false); |
| 266 | + HandlerMethodValidationException ex = new HandlerMethodValidationException(result); |
| 267 | + |
| 268 | + assertStatus(ex, HttpStatus.BAD_REQUEST); |
| 269 | + assertDetail(ex, "Validation failure"); |
| 270 | + |
| 271 | + reset(result); |
| 272 | + when(result.isForReturnValue()).thenReturn(true); |
| 273 | + ex = new HandlerMethodValidationException(result); |
| 274 | + |
| 275 | + assertStatus(ex, HttpStatus.INTERNAL_SERVER_ERROR); |
| 276 | + assertDetail(ex, "Validation failure"); |
| 277 | + } |
| 278 | + |
262 | 279 | @Test
|
263 | 280 | void unsupportedMediaTypeStatusException() {
|
264 | 281 |
|
@@ -360,15 +377,14 @@ void unsatisfiedRequestParameterException() {
|
360 | 377 | @Test
|
361 | 378 | void webExchangeBindException() {
|
362 | 379 |
|
363 |
| - MessageSourceTestHelper messageSourceHelper = new MessageSourceTestHelper(WebExchangeBindException.class); |
364 |
| - BindingResult bindingResult = messageSourceHelper.initBindingResult(); |
| 380 | + ValidationTestHelper testHelper = new ValidationTestHelper(WebExchangeBindException.class); |
| 381 | + BindingResult result = testHelper.bindingResult(); |
365 | 382 |
|
366 |
| - WebExchangeBindException ex = new WebExchangeBindException(this.methodParameter, bindingResult); |
| 383 | + WebExchangeBindException ex = new WebExchangeBindException(this.methodParameter, result); |
367 | 384 |
|
368 | 385 | assertStatus(ex, HttpStatus.BAD_REQUEST);
|
369 | 386 | assertDetail(ex, "Invalid request content.");
|
370 |
| - messageSourceHelper.assertDetailMessage(ex); |
371 |
| - messageSourceHelper.assertErrorMessages(ex::resolveErrorMessages); |
| 387 | + testHelper.assertMessages(ex, ex.getAllErrors()); |
372 | 388 |
|
373 | 389 | assertThat(ex.getHeaders()).isEmpty();
|
374 | 390 | }
|
@@ -434,59 +450,52 @@ private void assertDetailMessageCode(
|
434 | 450 | private void handle(String arg) {}
|
435 | 451 |
|
436 | 452 |
|
437 |
| - private static class MessageSourceTestHelper { |
| 453 | + private static class ValidationTestHelper { |
438 | 454 |
|
439 |
| - private final String code; |
| 455 | + private final BindingResult bindingResult; |
440 | 456 |
|
441 |
| - public MessageSourceTestHelper(Class<? extends ErrorResponse> exceptionType) { |
442 |
| - this.code = "problemDetail." + exceptionType.getName(); |
443 |
| - } |
| 457 | + private final StaticMessageSource messageSource = new StaticMessageSource(); |
| 458 | + |
| 459 | + public ValidationTestHelper(Class<? extends ErrorResponse> exceptionType) { |
444 | 460 |
|
445 |
| - public BindingResult initBindingResult() { |
446 |
| - BindingResult bindingResult = new BindException(new TestBean(), "myBean"); |
447 |
| - bindingResult.reject("bean.invalid.A", "Invalid bean message"); |
448 |
| - bindingResult.reject("bean.invalid.B"); |
449 |
| - bindingResult.rejectValue("name", "name.required", "must be provided"); |
450 |
| - bindingResult.rejectValue("age", "age.min"); |
451 |
| - return bindingResult; |
| 461 | + this.bindingResult = new BeanPropertyBindingResult(new TestBean(), "myBean"); |
| 462 | + this.bindingResult.reject("bean.invalid.A", "Invalid bean message"); |
| 463 | + this.bindingResult.reject("bean.invalid.B"); |
| 464 | + this.bindingResult.rejectValue("name", "name.required", "must be provided"); |
| 465 | + this.bindingResult.rejectValue("age", "age.min"); |
| 466 | + |
| 467 | + String code = "problemDetail." + exceptionType.getName(); |
| 468 | + this.messageSource.addMessage(code, Locale.UK, "Failed because {0}. Also because {1}"); |
| 469 | + this.messageSource.addMessage("bean.invalid.A", Locale.UK, "Bean A message"); |
| 470 | + this.messageSource.addMessage("bean.invalid.B", Locale.UK, "Bean B message"); |
| 471 | + this.messageSource.addMessage("name.required", Locale.UK, "name is required"); |
| 472 | + this.messageSource.addMessage("age.min", Locale.UK, "age is below minimum"); |
452 | 473 | }
|
453 | 474 |
|
454 |
| - private void assertDetailMessage(ErrorResponse ex) { |
| 475 | + public BindingResult bindingResult() { |
| 476 | + return this.bindingResult; |
| 477 | + } |
455 | 478 |
|
456 |
| - StaticMessageSource messageSource = initMessageSource(); |
| 479 | + private void assertMessages(ErrorResponse ex, List<? extends MessageSourceResolvable> errors) { |
457 | 480 |
|
458 |
| - String message = messageSource.getMessage( |
| 481 | + String message = this.messageSource.getMessage( |
459 | 482 | ex.getDetailMessageCode(), ex.getDetailMessageArguments(), Locale.UK);
|
460 | 483 |
|
461 | 484 | assertThat(message).isEqualTo(
|
462 | 485 | "Failed because Invalid bean message, and bean.invalid.B.myBean. " +
|
463 | 486 | "Also because name: must be provided, and age: age.min.myBean.age");
|
464 | 487 |
|
465 |
| - message = messageSource.getMessage( |
466 |
| - ex.getDetailMessageCode(), ex.getDetailMessageArguments(messageSource, Locale.UK), Locale.UK); |
| 488 | + message = this.messageSource.getMessage( |
| 489 | + ex.getDetailMessageCode(), ex.getDetailMessageArguments(this.messageSource, Locale.UK), Locale.UK); |
467 | 490 |
|
468 | 491 | assertThat(message).isEqualTo(
|
469 | 492 | "Failed because Bean A message, and Bean B message. " +
|
470 | 493 | "Also because name is required, and age is below minimum");
|
471 |
| - } |
472 |
| - |
473 |
| - private void assertErrorMessages(BiFunction<MessageSource, Locale, Map<ObjectError, String>> expectedMessages) { |
474 |
| - StaticMessageSource messageSource = initMessageSource(); |
475 |
| - Map<ObjectError, String> map = expectedMessages.apply(messageSource, Locale.UK); |
476 | 494 |
|
477 |
| - assertThat(map).hasSize(4).containsValues( |
478 |
| - "Bean A message", "Bean B message", "name is required", "age is below minimum"); |
| 495 | + assertThat(BindErrorUtils.resolve(errors, this.messageSource, Locale.UK)).hasSize(4) |
| 496 | + .containsValues("Bean A message", "Bean B message", "name is required", "age is below minimum"); |
479 | 497 | }
|
480 | 498 |
|
481 |
| - private StaticMessageSource initMessageSource() { |
482 |
| - StaticMessageSource messageSource = new StaticMessageSource(); |
483 |
| - messageSource.addMessage(this.code, Locale.UK, "Failed because {0}. Also because {1}"); |
484 |
| - messageSource.addMessage("bean.invalid.A", Locale.UK, "Bean A message"); |
485 |
| - messageSource.addMessage("bean.invalid.B", Locale.UK, "Bean B message"); |
486 |
| - messageSource.addMessage("name.required", Locale.UK, "name is required"); |
487 |
| - messageSource.addMessage("age.min", Locale.UK, "age is below minimum"); |
488 |
| - return messageSource; |
489 |
| - } |
490 | 499 | }
|
491 | 500 |
|
492 | 501 | }
|
0 commit comments