From 4e70e4c81d42486741f6d75ca5df0bdcea029680 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 5 Aug 2020 18:17:06 +0200 Subject: [PATCH] Regression tests for @RestControllerAdvice support in MockMvc This commit introduces regression tests for @RestControllerAdvice support in standalone MockMvc configurations. See gh-25520 --- .../standalone/ExceptionHandlerTests.java | 130 +++++++++++++++++- 1 file changed, 124 insertions(+), 6 deletions(-) diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/ExceptionHandlerTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/ExceptionHandlerTests.java index 8ee2d162532b..504927e47036 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/ExceptionHandlerTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/ExceptionHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,16 @@ import org.junit.Test; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.RestControllerAdvice; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @@ -32,19 +37,20 @@ * Exception handling via {@code @ExceptionHandler} method. * * @author Rossen Stoyanchev + * @author Sam Brannen */ public class ExceptionHandlerTests { @Test - public void testExceptionHandlerMethod() throws Exception { + public void mvcLocalExceptionHandlerMethod() throws Exception { standaloneSetup(new PersonController()).build() - .perform(get("/person/Clyde")) + .perform(get("/person/Clyde")) .andExpect(status().isOk()) .andExpect(forwardedUrl("errorView")); } @Test - public void testGlobalExceptionHandlerMethod() throws Exception { + public void mvcGlobalExceptionHandlerMethod() throws Exception { standaloneSetup(new PersonController()).setControllerAdvice(new GlobalExceptionHandler()).build() .perform(get("/person/Bonnie")) .andExpect(status().isOk()) @@ -52,13 +58,60 @@ public void testGlobalExceptionHandlerMethod() throws Exception { } @Test - public void testGlobalExceptionHandlerMethodUsingClassArgument() throws Exception { + public void mvcGlobalExceptionHandlerMethodUsingClassArgument() throws Exception { standaloneSetup(PersonController.class).setControllerAdvice(GlobalExceptionHandler.class).build() .perform(get("/person/Bonnie")) .andExpect(status().isOk()) .andExpect(forwardedUrl("globalErrorView")); } + @Test + public void restNoException() throws Exception { + standaloneSetup(RestPersonController.class) + .setControllerAdvice(RestGlobalExceptionHandler.class, RestPersonControllerExceptionHandler.class).build() + .perform(get("/person/Yoda").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.name").value("Yoda")); + } + + @Test + public void restLocalExceptionHandlerMethod() throws Exception { + standaloneSetup(RestPersonController.class) + .setControllerAdvice(RestGlobalExceptionHandler.class, RestPersonControllerExceptionHandler.class).build() + .perform(get("/person/Luke").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.error").value("local - IllegalArgumentException")); + } + + @Test + public void restGlobalExceptionHandlerMethod() throws Exception { + standaloneSetup(RestPersonController.class) + .setControllerAdvice(RestGlobalExceptionHandler.class).build() + .perform(get("/person/Leia").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.error").value("global - IllegalStateException")); + } + + @Test + public void restGlobalRestPersonControllerExceptionHandlerTakesPrecedenceOverGlobalExceptionHandler() throws Exception { + standaloneSetup(RestPersonController.class) + .setControllerAdvice(RestGlobalExceptionHandler.class, RestPersonControllerExceptionHandler.class).build() + .perform(get("/person/Leia").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.error").value("globalPersonController - IllegalStateException")); + } + + @Test // gh-25520 + public void restNoHandlerFound() throws Exception { + standaloneSetup(RestPersonController.class) + .setControllerAdvice(RestGlobalExceptionHandler.class, RestPersonControllerExceptionHandler.class) + .addDispatcherServletCustomizer(dispatcherServlet -> dispatcherServlet.setThrowExceptionIfNoHandlerFound(true)) + .build() + .perform(get("/bogus").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.error").value("global - NoHandlerFoundException")); + } + @Controller private static class PersonController { @@ -80,7 +133,6 @@ public String handleException(IllegalArgumentException exception) { } } - @ControllerAdvice private static class GlobalExceptionHandler { @@ -88,7 +140,73 @@ private static class GlobalExceptionHandler { public String handleException(IllegalStateException exception) { return "globalErrorView"; } + } + + @RestController + private static class RestPersonController { + + @GetMapping("/person/{name}") + Person get(@PathVariable String name) { + switch (name) { + case "Luke": + throw new IllegalArgumentException(); + case "Leia": + throw new IllegalStateException(); + default: + return new Person("Yoda"); + } + } + @ExceptionHandler + Error handleException(IllegalArgumentException exception) { + return new Error("local - " + exception.getClass().getSimpleName()); + } + } + + @RestControllerAdvice(assignableTypes = RestPersonController.class) + @Order(Ordered.HIGHEST_PRECEDENCE) + private static class RestPersonControllerExceptionHandler { + + @ExceptionHandler + Error handleException(Throwable exception) { + return new Error("globalPersonController - " + exception.getClass().getSimpleName()); + } + } + + @RestControllerAdvice + @Order(Ordered.LOWEST_PRECEDENCE) + private static class RestGlobalExceptionHandler { + + @ExceptionHandler + Error handleException(Throwable exception) { + return new Error( "global - " + exception.getClass().getSimpleName()); + } + } + + static class Person { + + private final String name; + + Person(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + static class Error { + + private final String error; + + Error(String error) { + this.error = error; + } + + public String getError() { + return error; + } } }