1
1
/*
2
- * Copyright 2002-2019 the original author or authors.
2
+ * Copyright 2002-2020 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
16
16
17
17
package org .springframework .test .web .servlet .samples .standalone ;
18
18
19
+ import org .junit .jupiter .api .Nested ;
19
20
import org .junit .jupiter .api .Test ;
20
21
22
+ import org .springframework .core .Ordered ;
23
+ import org .springframework .core .annotation .Order ;
24
+ import org .springframework .http .MediaType ;
21
25
import org .springframework .stereotype .Controller ;
22
26
import org .springframework .web .bind .annotation .ControllerAdvice ;
23
27
import org .springframework .web .bind .annotation .ExceptionHandler ;
24
28
import org .springframework .web .bind .annotation .GetMapping ;
25
29
import org .springframework .web .bind .annotation .PathVariable ;
30
+ import org .springframework .web .bind .annotation .RestController ;
31
+ import org .springframework .web .bind .annotation .RestControllerAdvice ;
26
32
27
33
import static org .springframework .test .web .servlet .request .MockMvcRequestBuilders .get ;
28
34
import static org .springframework .test .web .servlet .result .MockMvcResultMatchers .forwardedUrl ;
35
+ import static org .springframework .test .web .servlet .result .MockMvcResultMatchers .jsonPath ;
29
36
import static org .springframework .test .web .servlet .result .MockMvcResultMatchers .status ;
30
37
import static org .springframework .test .web .servlet .setup .MockMvcBuilders .standaloneSetup ;
31
38
32
39
/**
33
- * Exception handling via {@code @ExceptionHandler} method .
40
+ * Exception handling via {@code @ExceptionHandler} methods .
34
41
*
35
42
* @author Rossen Stoyanchev
43
+ * @author Sam Brannen
36
44
*/
37
- public class ExceptionHandlerTests {
45
+ class ExceptionHandlerTests {
38
46
39
- @ Test
40
- public void testExceptionHandlerMethod () throws Exception {
41
- standaloneSetup (new PersonController ()).build ()
42
- .perform (get ("/person/Clyde" ))
47
+ @ Nested
48
+ class MvcTests {
49
+
50
+ @ Test
51
+ void localExceptionHandlerMethod () throws Exception {
52
+ standaloneSetup (new PersonController ()).build ()
53
+ .perform (get ("/person/Clyde" ))
43
54
.andExpect (status ().isOk ())
44
55
.andExpect (forwardedUrl ("errorView" ));
45
- }
56
+ }
46
57
47
- @ Test
48
- public void testGlobalExceptionHandlerMethod () throws Exception {
49
- standaloneSetup (new PersonController ()).setControllerAdvice (new GlobalExceptionHandler ()).build ()
58
+ @ Test
59
+ void globalExceptionHandlerMethod () throws Exception {
60
+ standaloneSetup (new PersonController ()).setControllerAdvice (new GlobalExceptionHandler ()).build ()
50
61
.perform (get ("/person/Bonnie" ))
51
62
.andExpect (status ().isOk ())
52
63
.andExpect (forwardedUrl ("globalErrorView" ));
53
- }
64
+ }
54
65
55
- @ Test
56
- public void testGlobalExceptionHandlerMethodUsingClassArgument () throws Exception {
57
- standaloneSetup (PersonController .class ).setControllerAdvice (GlobalExceptionHandler .class ).build ()
66
+ @ Test
67
+ void globalExceptionHandlerMethodUsingClassArgument () throws Exception {
68
+ standaloneSetup (PersonController .class ).setControllerAdvice (GlobalExceptionHandler .class ).build ()
58
69
.perform (get ("/person/Bonnie" ))
59
70
.andExpect (status ().isOk ())
60
71
.andExpect (forwardedUrl ("globalErrorView" ));
72
+ }
61
73
}
62
74
63
75
64
76
@ Controller
65
77
private static class PersonController {
66
78
67
79
@ GetMapping ("/person/{name}" )
68
- public String show (@ PathVariable String name ) {
80
+ String show (@ PathVariable String name ) {
69
81
if (name .equals ("Clyde" )) {
70
82
throw new IllegalArgumentException ("simulated exception" );
71
83
}
@@ -76,20 +88,138 @@ else if (name.equals("Bonnie")) {
76
88
}
77
89
78
90
@ ExceptionHandler
79
- public String handleException (IllegalArgumentException exception ) {
91
+ String handleException (IllegalArgumentException exception ) {
80
92
return "errorView" ;
81
93
}
82
94
}
83
95
84
-
85
96
@ ControllerAdvice
86
97
private static class GlobalExceptionHandler {
87
98
88
99
@ ExceptionHandler
89
- public String handleException (IllegalStateException exception ) {
100
+ String handleException (IllegalStateException exception ) {
90
101
return "globalErrorView" ;
91
102
}
103
+ }
104
+
105
+
106
+ @ Nested
107
+ class RestTests {
108
+
109
+ @ Test
110
+ void noException () throws Exception {
111
+ standaloneSetup (RestPersonController .class )
112
+ .setControllerAdvice (RestGlobalExceptionHandler .class , RestPersonControllerExceptionHandler .class ).build ()
113
+ .perform (get ("/person/Yoda" ).accept (MediaType .APPLICATION_JSON ))
114
+ .andExpect (status ().isOk ())
115
+ .andExpect (jsonPath ("$.name" ).value ("Yoda" ));
116
+ }
117
+
118
+ @ Test
119
+ void localExceptionHandlerMethod () throws Exception {
120
+ standaloneSetup (RestPersonController .class )
121
+ .setControllerAdvice (RestGlobalExceptionHandler .class , RestPersonControllerExceptionHandler .class ).build ()
122
+ .perform (get ("/person/Luke" ).accept (MediaType .APPLICATION_JSON ))
123
+ .andExpect (status ().isOk ())
124
+ .andExpect (jsonPath ("$.error" ).value ("local - IllegalArgumentException" ));
125
+ }
126
+
127
+ @ Test
128
+ void globalExceptionHandlerMethod () throws Exception {
129
+ standaloneSetup (RestPersonController .class )
130
+ .setControllerAdvice (RestGlobalExceptionHandler .class ).build ()
131
+ .perform (get ("/person/Leia" ).accept (MediaType .APPLICATION_JSON ))
132
+ .andExpect (status ().isOk ())
133
+ .andExpect (jsonPath ("$.error" ).value ("global - IllegalStateException" ));
134
+ }
135
+
136
+ @ Test
137
+ void globalRestPersonControllerExceptionHandlerTakesPrecedenceOverGlobalExceptionHandler () throws Exception {
138
+ standaloneSetup (RestPersonController .class )
139
+ .setControllerAdvice (RestGlobalExceptionHandler .class , RestPersonControllerExceptionHandler .class ).build ()
140
+ .perform (get ("/person/Leia" ).accept (MediaType .APPLICATION_JSON ))
141
+ .andExpect (status ().isOk ())
142
+ .andExpect (jsonPath ("$.error" ).value ("globalPersonController - IllegalStateException" ));
143
+ }
144
+
145
+ @ Test // gh-25520
146
+ void noHandlerFound () throws Exception {
147
+ standaloneSetup (RestPersonController .class )
148
+ .setControllerAdvice (RestGlobalExceptionHandler .class , RestPersonControllerExceptionHandler .class )
149
+ .addDispatcherServletCustomizer (dispatcherServlet -> dispatcherServlet .setThrowExceptionIfNoHandlerFound (true ))
150
+ .build ()
151
+ .perform (get ("/bogus" ).accept (MediaType .APPLICATION_JSON ))
152
+ .andExpect (status ().isOk ())
153
+ .andExpect (jsonPath ("$.error" ).value ("global - NoHandlerFoundException" ));
154
+ }
155
+ }
156
+
157
+
158
+ @ RestController
159
+ private static class RestPersonController {
160
+
161
+ @ GetMapping ("/person/{name}" )
162
+ Person get (@ PathVariable String name ) {
163
+ switch (name ) {
164
+ case "Luke" :
165
+ throw new IllegalArgumentException ();
166
+ case "Leia" :
167
+ throw new IllegalStateException ();
168
+ default :
169
+ return new Person ("Yoda" );
170
+ }
171
+ }
172
+
173
+ @ ExceptionHandler
174
+ Error handleException (IllegalArgumentException exception ) {
175
+ return new Error ("local - " + exception .getClass ().getSimpleName ());
176
+ }
177
+ }
178
+
179
+ @ RestControllerAdvice (assignableTypes = RestPersonController .class )
180
+ @ Order (Ordered .HIGHEST_PRECEDENCE )
181
+ private static class RestPersonControllerExceptionHandler {
92
182
183
+ @ ExceptionHandler
184
+ Error handleException (Throwable exception ) {
185
+ return new Error ("globalPersonController - " + exception .getClass ().getSimpleName ());
186
+ }
187
+ }
188
+
189
+ @ RestControllerAdvice
190
+ @ Order (Ordered .LOWEST_PRECEDENCE )
191
+ private static class RestGlobalExceptionHandler {
192
+
193
+ @ ExceptionHandler
194
+ Error handleException (Throwable exception ) {
195
+ return new Error ( "global - " + exception .getClass ().getSimpleName ());
196
+ }
197
+ }
198
+
199
+ static class Person {
200
+
201
+ private final String name ;
202
+
203
+ Person (String name ) {
204
+ this .name = name ;
205
+ }
206
+
207
+ public String getName () {
208
+ return name ;
209
+ }
210
+ }
211
+
212
+ static class Error {
213
+
214
+ private final String error ;
215
+
216
+ Error (String error ) {
217
+ this .error = error ;
218
+ }
219
+
220
+ public String getError () {
221
+ return error ;
222
+ }
93
223
}
94
224
95
225
}
0 commit comments