From 2dd587596437a4bbe9f62ba0dc9f7b13382fb533 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 25 Feb 2015 16:57:58 -0500 Subject: [PATCH] Support @ControllerAdvice in StandaloneMockMvcBuilder Issue: SPR-12751 --- .../setup/StandaloneMockMvcBuilder.java | 41 +++++++++++++++++-- .../setup/StubWebApplicationContext.java | 3 ++ .../standalone/ExceptionHandlerTests.java | 36 +++++++++++++--- .../resultmatchers/ModelAssertionTests.java | 18 +++++++- .../WebMvcConfigurationSupport.java | 7 ++++ 5 files changed, 94 insertions(+), 11 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StandaloneMockMvcBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StandaloneMockMvcBuilder.java index 1128e7c7e5f..f298941f2a0 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StandaloneMockMvcBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StandaloneMockMvcBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 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. @@ -27,6 +27,7 @@ import java.util.Map; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanInitializationException; import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContextAware; import org.springframework.format.support.DefaultFormattingConversionService; import org.springframework.format.support.FormattingConversionService; import org.springframework.http.converter.HttpMessageConverter; @@ -86,6 +87,8 @@ public class StandaloneMockMvcBuilder extends AbstractMockMvcBuilder controllerAdvice; + private List> messageConverters = new ArrayList>(); private List customArgumentResolvers = new ArrayList(); @@ -100,7 +103,7 @@ public class StandaloneMockMvcBuilder extends AbstractMockMvcBuilder handlerExceptionResolvers = new ArrayList(); + private List handlerExceptionResolvers; private Long asyncRequestTimeout; @@ -128,6 +131,19 @@ public class StandaloneMockMvcBuilder extends AbstractMockMvcBuilderNormally {@code @ControllerAdvice} are auto-detected. However since the + * standalone setup does not load Spring configuration they need to be + * registered explicitly instead. + * @since 4.2 + */ + public StandaloneMockMvcBuilder setControllerAdvice(Object... controllerAdvice) { + this.controllerAdvice = Arrays.asList(controllerAdvice); + return this; + } + /** * Set the message converters to use in argument resolvers and in return value * handlers, which support reading and/or writing to the body of the request @@ -317,6 +333,9 @@ public class StandaloneMockMvcBuilder extends AbstractMockMvcBuilder exceptionResolvers) { - exceptionResolvers.addAll(StandaloneMockMvcBuilder.this.handlerExceptionResolvers); + if (handlerExceptionResolvers == null) { + return; + } + for (HandlerExceptionResolver resolver : handlerExceptionResolvers) { + if (resolver instanceof ApplicationContextAware) { + ((ApplicationContextAware) resolver).setApplicationContext(getApplicationContext()); + } + if (resolver instanceof InitializingBean) { + try { + ((InitializingBean) resolver).afterPropertiesSet(); + } + catch (Exception ex) { + throw new IllegalStateException("Failure from afterPropertiesSet", ex); + } + } + exceptionResolvers.add(resolver); + } } } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java index c98e4752f1e..5338455b85d 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/StubWebApplicationContext.java @@ -137,6 +137,9 @@ class StubWebApplicationContext implements WebApplicationContext { } public void addBeans(List beans) { + if (beans == null) { + return; + } for (Object bean : beans) { String name = bean.getClass().getName() + "#" + ObjectUtils.getIdentityHexString(bean); this.beanFactory.addBean(name, bean); 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 e43932a0009..ca098b6fe73 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-2012 the original author or authors. + * Copyright 2002-2015 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. @@ -16,18 +16,19 @@ package org.springframework.test.web.servlet.samples.standalone; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*; + import org.junit.Test; 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.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; -import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*; - /** * Exception handling via {@code @ExceptionHandler} method. * @@ -35,6 +36,7 @@ import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*; */ public class ExceptionHandlerTests { + @Test public void testExceptionHandlerMethod() throws Exception { standaloneSetup(new PersonController()).build() @@ -43,6 +45,14 @@ public class ExceptionHandlerTests { .andExpect(forwardedUrl("errorView")); } + @Test + public void testGlobalExceptionHandlerMethod() throws Exception { + standaloneSetup(new PersonController()).setControllerAdvice(new GlobalExceptionHandler()).build() + .perform(get("/person/Bonnie")) + .andExpect(status().isOk()) + .andExpect(forwardedUrl("globalErrorView")); + } + @Controller private static class PersonController { @@ -50,7 +60,10 @@ public class ExceptionHandlerTests { @RequestMapping(value="/person/{name}", method=RequestMethod.GET) public String show(@PathVariable String name) { if (name.equals("Clyde")) { - throw new IllegalArgumentException("Black listed"); + throw new IllegalArgumentException("simulated exception"); + } + else if (name.equals("Bonnie")) { + throw new IllegalStateException("simulated exception"); } return "person/show"; } @@ -60,4 +73,15 @@ public class ExceptionHandlerTests { return "errorView"; } } + + @ControllerAdvice + private static class GlobalExceptionHandler { + + @ExceptionHandler + public String handleException(IllegalStateException exception) { + return "globalErrorView"; + } + + } + } diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/ModelAssertionTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/ModelAssertionTests.java index 14ab3e767e5..e4c3998ed4b 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/ModelAssertionTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/ModelAssertionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 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. @@ -26,6 +26,8 @@ import org.springframework.test.web.Person; import org.springframework.test.web.servlet.MockMvc; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -43,6 +45,7 @@ public class ModelAssertionTests { private MockMvc mockMvc; + @Before public void setup() { @@ -51,6 +54,7 @@ public class ModelAssertionTests { this.mockMvc = standaloneSetup(controller) .defaultRequest(get("/")) .alwaysExpect(status().isOk()) + .setControllerAdvice(new ModelAttributeAdvice()) .build(); } @@ -60,7 +64,8 @@ public class ModelAssertionTests { .andExpect(model().attribute("integer", 3)) .andExpect(model().attribute("string", "a string value")) .andExpect(model().attribute("integer", equalTo(3))) // Hamcrest... - .andExpect(model().attribute("string", equalTo("a string value"))); + .andExpect(model().attribute("string", equalTo("a string value"))) + .andExpect(model().attribute("globalAttrName", equalTo("Global Attribute Value"))); } @Test @@ -113,4 +118,13 @@ public class ModelAssertionTests { } } + @ControllerAdvice + private static class ModelAttributeAdvice { + + @ModelAttribute("globalAttrName") + public String getAttribute() { + return "Global Attribute Value"; + } + } + } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java index 713ca69e787..aea2bc348ff 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.java @@ -207,6 +207,10 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv this.applicationContext = applicationContext; } + public ApplicationContext getApplicationContext() { + return this.applicationContext; + } + /** * Set the {@link javax.servlet.ServletContext}, e.g. for resource handling, * looking up file extensions, etc. @@ -216,6 +220,9 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv this.servletContext = servletContext; } + public ServletContext getServletContext() { + return this.servletContext; + } /** * Return a {@link RequestMappingHandlerMapping} ordered at 0 for mapping