@ -23,22 +23,23 @@ import java.util.List;
@@ -23,22 +23,23 @@ import java.util.List;
import jakarta.annotation.Priority ;
import org.junit.jupiter.api.Test ;
import org.springframework.beans.BeanUtils ;
import org.springframework.beans.factory.BeanFactory ;
import org.springframework.context.annotation.AnnotationConfigApplicationContext ;
import org.springframework.context.annotation.Bean ;
import org.springframework.context.annotation.Configuration ;
import org.springframework.context.support.StaticApplicationContext ;
import org.springframework.core.Ordered ;
import org.springframework.core.PriorityOrdered ;
import org.springframework.core.annotation.AnnotatedElementUtils ;
import org.springframework.core.annotation.Order ;
import org.springframework.web.bind.annotation.ControllerAdvice ;
import org.springframework.web.bind.annotation.RestController ;
import static org.assertj.core.api.Assertions.assertThat ;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException ;
import static org.mockito.ArgumentMatchers.eq ;
import static org.mockito.BDDMockito.given ;
import static org.mockito.Mockito.mock ;
import static org.mockito.Mockito.verify ;
/ * *
* Unit and integration tests for { @link ControllerAdviceBean } .
@ -48,52 +49,50 @@ import static org.mockito.Mockito.verify;
@@ -48,52 +49,50 @@ import static org.mockito.Mockito.verify;
* /
class ControllerAdviceBeanTests {
@Test
void constructorPreconditions ( ) {
assertThatIllegalArgumentException ( )
. isThrownBy ( ( ) - > new ControllerAdviceBean ( null ) )
. withMessage ( "Bean must not be null" ) ;
assertThatIllegalArgumentException ( )
. isThrownBy ( ( ) - > new ControllerAdviceBean ( null , null ) )
. withMessage ( "Bean name must contain text" ) ;
private StaticApplicationContext applicationContext = new StaticApplicationContext ( ) ;
@Test
void shouldFailForNullOrEmptyBeanName ( ) {
assertThatIllegalArgumentException ( )
. isThrownBy ( ( ) - > new ControllerAdviceBean ( "" , null ) )
. isThrownBy ( ( ) - > new ControllerAdviceBean ( null , null , null ) )
. withMessage ( "Bean name must contain text" ) ;
assertThatIllegalArgumentException ( )
. isThrownBy ( ( ) - > new ControllerAdviceBean ( "\t" , null ) )
. isThrownBy ( ( ) - > new ControllerAdviceBean ( " " , null , null ) )
. withMessage ( "Bean name must contain text" ) ;
}
@Test
void shouldFailForNullBeanFactory ( ) {
assertThatIllegalArgumentException ( )
. isThrownBy ( ( ) - > new ControllerAdviceBean ( "myBean" , null ) )
. isThrownBy ( ( ) - > new ControllerAdviceBean ( "beanName" , null , null ) )
. withMessage ( "BeanFactory must not be null" ) ;
}
@Test
void equalsHashCodeAndToStringForBeanName ( ) {
String beanName = "myBean" ;
BeanFactory beanFactory = mock ( ) ;
given ( beanFactory . containsBean ( beanName ) ) . willReturn ( true ) ;
ControllerAdviceBean bean1 = new ControllerAdviceBean ( beanName , beanFactory ) ;
ControllerAdviceBean bean2 = new ControllerAdviceBean ( beanName , beanFactory ) ;
assertEqualsHashCodeAndToString ( bean1 , bean2 , beanName ) ;
void shouldFailWhenBeanFactoryDoesNotContainBean ( ) {
BeanFactory beanFactory = mock ( BeanFactory . class ) ;
given ( beanFactory . containsBean ( eq ( "beanName" ) ) ) . willReturn ( false ) ;
assertThatIllegalArgumentException ( )
. isThrownBy ( ( ) - > new ControllerAdviceBean ( "beanName" , beanFactory , null ) )
. withMessageContaining ( "does not contain specified controller advice bean 'beanName'" ) ;
}
@Test
void equalsHashCodeAndToStringForBeanInstance ( ) {
String toString = "beanInstance" ;
Object beanInstance = new Object ( ) {
@Override
public String toString ( ) {
return toString ;
void shouldFailWhenControllerAdviceNull ( ) {
BeanFactory beanFactory = mock ( BeanFactory . class ) ;
given ( beanFactory . containsBean ( eq ( "beanName" ) ) ) . willReturn ( true ) ;
assertThatIllegalArgumentException ( )
. isThrownBy ( ( ) - > new ControllerAdviceBean ( "beanName" , beanFactory , null ) )
. withMessage ( "ControllerAdvice must not be null" ) ;
}
} ;
ControllerAdviceBean bean1 = new ControllerAdviceBean ( beanInstance ) ;
ControllerAdviceBean bean2 = new ControllerAdviceBean ( beanInstance ) ;
assertEqualsHashCodeAndToString ( bean1 , bean2 , toString ) ;
@Test
void equalsHashCodeAndToStringForBeanName ( ) {
String beanName = SimpleControllerAdvice . class . getSimpleName ( ) ;
ControllerAdviceBean bean1 = createControllerAdviceBean ( SimpleControllerAdvice . class ) ;
ControllerAdviceBean bean2 = createControllerAdviceBean ( SimpleControllerAdvice . class ) ;
assertEqualsHashCodeAndToString ( bean1 , bean2 , beanName ) ;
}
@Test
@ -103,7 +102,7 @@ class ControllerAdviceBeanTests {
@@ -103,7 +102,7 @@ class ControllerAdviceBeanTests {
@Test
void orderedWithLowestPrecedenceByDefaultForBeanInstance ( ) {
assertOrder ( new SimpleControllerAdvice ( ) , Ordered . LOWEST_PRECEDENCE ) ;
assertOrder ( SimpleControllerAdvice . class , Ordered . LOWEST_PRECEDENCE ) ;
}
@Test
@ -113,7 +112,7 @@ class ControllerAdviceBeanTests {
@@ -113,7 +112,7 @@ class ControllerAdviceBeanTests {
@Test
void orderedViaOrderedInterfaceForBeanInstance ( ) {
assertOrder ( new OrderedControllerAdvice ( ) , 42 ) ;
assertOrder ( OrderedControllerAdvice . class , 42 ) ;
}
@Test
@ -124,13 +123,13 @@ class ControllerAdviceBeanTests {
@@ -124,13 +123,13 @@ class ControllerAdviceBeanTests {
@Test
void orderedViaAnnotationForBeanInstance ( ) {
assertOrder ( new OrderAnnotationControllerAdvice ( ) , 100 ) ;
assertOrder ( new PriorityAnnotationControllerAdvice ( ) , 200 ) ;
assertOrder ( OrderAnnotationControllerAdvice . class , 100 ) ;
assertOrder ( PriorityAnnotationControllerAdvice . class , 200 ) ;
}
@Test
void shouldMatchAll ( ) {
ControllerAdviceBean bean = new ControllerAdviceBean ( new SimpleControllerAdvice ( ) ) ;
ControllerAdviceBean bean = createControllerAdviceBean ( SimpleControllerAdvice . class ) ;
assertApplicable ( "should match all" , bean , AnnotatedController . class ) ;
assertApplicable ( "should match all" , bean , ImplementationController . class ) ;
assertApplicable ( "should match all" , bean , InheritanceController . class ) ;
@ -139,7 +138,7 @@ class ControllerAdviceBeanTests {
@@ -139,7 +138,7 @@ class ControllerAdviceBeanTests {
@Test
void basePackageSupport ( ) {
ControllerAdviceBean bean = new ControllerAdviceBean ( new BasePackageSupport ( ) ) ;
ControllerAdviceBean bean = createControllerAdviceBean ( BasePackageSupport . class ) ;
assertApplicable ( "base package support" , bean , AnnotatedController . class ) ;
assertApplicable ( "base package support" , bean , ImplementationController . class ) ;
assertApplicable ( "base package support" , bean , InheritanceController . class ) ;
@ -148,7 +147,7 @@ class ControllerAdviceBeanTests {
@@ -148,7 +147,7 @@ class ControllerAdviceBeanTests {
@Test
void basePackageValueSupport ( ) {
ControllerAdviceBean bean = new ControllerAdviceBean ( new BasePackageValueSupport ( ) ) ;
ControllerAdviceBean bean = createControllerAdviceBean ( BasePackageValueSupport . class ) ;
assertApplicable ( "base package support" , bean , AnnotatedController . class ) ;
assertApplicable ( "base package support" , bean , ImplementationController . class ) ;
assertApplicable ( "base package support" , bean , InheritanceController . class ) ;
@ -157,14 +156,14 @@ class ControllerAdviceBeanTests {
@@ -157,14 +156,14 @@ class ControllerAdviceBeanTests {
@Test
void annotationSupport ( ) {
ControllerAdviceBean bean = new ControllerAdviceBean ( new AnnotationSupport ( ) ) ;
ControllerAdviceBean bean = createControllerAdviceBean ( AnnotationSupport . class ) ;
assertApplicable ( "annotation support" , bean , AnnotatedController . class ) ;
assertNotApplicable ( "this bean is not annotated" , bean , InheritanceController . class ) ;
}
@Test
void markerClassSupport ( ) {
ControllerAdviceBean bean = new ControllerAdviceBean ( new MarkerClassSupport ( ) ) ;
ControllerAdviceBean bean = createControllerAdviceBean ( MarkerClassSupport . class ) ;
assertApplicable ( "base package class support" , bean , AnnotatedController . class ) ;
assertApplicable ( "base package class support" , bean , ImplementationController . class ) ;
assertApplicable ( "base package class support" , bean , InheritanceController . class ) ;
@ -173,7 +172,7 @@ class ControllerAdviceBeanTests {
@@ -173,7 +172,7 @@ class ControllerAdviceBeanTests {
@Test
void shouldNotMatch ( ) {
ControllerAdviceBean bean = new ControllerAdviceBean ( new ShouldNotMatch ( ) ) ;
ControllerAdviceBean bean = createControllerAdviceBean ( ShouldNotMatch . class ) ;
assertNotApplicable ( "should not match" , bean , AnnotatedController . class ) ;
assertNotApplicable ( "should not match" , bean , ImplementationController . class ) ;
assertNotApplicable ( "should not match" , bean , InheritanceController . class ) ;
@ -182,7 +181,7 @@ class ControllerAdviceBeanTests {
@@ -182,7 +181,7 @@ class ControllerAdviceBeanTests {
@Test
void assignableTypesSupport ( ) {
ControllerAdviceBean bean = new ControllerAdviceBean ( new AssignableTypesSupport ( ) ) ;
ControllerAdviceBean bean = createControllerAdviceBean ( AssignableTypesSupport . class ) ;
assertApplicable ( "controller implements assignable" , bean , ImplementationController . class ) ;
assertApplicable ( "controller inherits assignable" , bean , InheritanceController . class ) ;
assertNotApplicable ( "not assignable" , bean , AnnotatedController . class ) ;
@ -191,7 +190,7 @@ class ControllerAdviceBeanTests {
@@ -191,7 +190,7 @@ class ControllerAdviceBeanTests {
@Test
void multipleMatch ( ) {
ControllerAdviceBean bean = new ControllerAdviceBean ( new MultipleSelectorsSupport ( ) ) ;
ControllerAdviceBean bean = create ControllerAdviceBean( MultipleSelectorsSupport . class ) ;
assertApplicable ( "controller implements assignable" , bean , ImplementationController . class ) ;
assertApplicable ( "controller is annotated" , bean , AnnotatedController . class ) ;
assertNotApplicable ( "should not match" , bean , InheritanceController . class ) ;
@ -217,6 +216,13 @@ class ControllerAdviceBeanTests {
@@ -217,6 +216,13 @@ class ControllerAdviceBeanTests {
assertThat ( adviceBeans ) . extracting ( ControllerAdviceBean : : getBeanType ) . containsExactly ( expectedTypes ) ;
}
private ControllerAdviceBean createControllerAdviceBean ( Class < ? > beanType ) {
String beanName = beanType . getSimpleName ( ) ;
this . applicationContext . registerSingleton ( beanName , beanType ) ;
ControllerAdvice controllerAdvice = AnnotatedElementUtils . findMergedAnnotation ( beanType , ControllerAdvice . class ) ;
return new ControllerAdviceBean ( beanName , this . applicationContext , controllerAdvice ) ;
}
private void assertEqualsHashCodeAndToString ( ControllerAdviceBean bean1 , ControllerAdviceBean bean2 , String toString ) {
assertThat ( bean1 ) . isEqualTo ( bean2 ) ;
assertThat ( bean2 ) . isEqualTo ( bean1 ) ;
@ -225,24 +231,8 @@ class ControllerAdviceBeanTests {
@@ -225,24 +231,8 @@ class ControllerAdviceBeanTests {
assertThat ( bean2 . toString ( ) ) . isEqualTo ( toString ) ;
}
private void assertOrder ( Object bean , int expectedOrder ) {
assertThat ( new ControllerAdviceBean ( bean ) . getOrder ( ) ) . isEqualTo ( expectedOrder ) ;
}
@SuppressWarnings ( { "rawtypes" , "unchecked" } )
private void assertOrder ( Class beanType , int expectedOrder ) {
String beanName = "myBean" ;
BeanFactory beanFactory = mock ( ) ;
given ( beanFactory . containsBean ( beanName ) ) . willReturn ( true ) ;
given ( beanFactory . getType ( beanName ) ) . willReturn ( beanType ) ;
given ( beanFactory . getBean ( beanName ) ) . willReturn ( BeanUtils . instantiateClass ( beanType ) ) ;
ControllerAdviceBean controllerAdviceBean = new ControllerAdviceBean ( beanName , beanFactory ) ;
assertThat ( controllerAdviceBean . getOrder ( ) ) . isEqualTo ( expectedOrder ) ;
verify ( beanFactory ) . containsBean ( beanName ) ;
verify ( beanFactory ) . getType ( beanName ) ;
verify ( beanFactory ) . getBean ( beanName ) ;
private void assertOrder ( Class < ? > beanType , int expectedOrder ) {
assertThat ( createControllerAdviceBean ( beanType ) . getOrder ( ) ) . isEqualTo ( expectedOrder ) ;
}
private void assertApplicable ( String message , ControllerAdviceBean controllerAdvice , Class < ? > controllerBeanType ) {
@ -259,18 +249,22 @@ class ControllerAdviceBeanTests {
@@ -259,18 +249,22 @@ class ControllerAdviceBeanTests {
// ControllerAdvice classes
@ControllerAdvice
static class SimpleControllerAdvice { }
static class SimpleControllerAdvice {
}
@ControllerAdvice
static class SimpleControllerAdviceWithBeanOrder { }
static class SimpleControllerAdviceWithBeanOrder {
}
@ControllerAdvice
@Order ( 100 )
static class OrderAnnotationControllerAdvice { }
static class OrderAnnotationControllerAdvice {
}
@ControllerAdvice
@Priority ( 200 )
static class PriorityAnnotationControllerAdvice { }
static class PriorityAnnotationControllerAdvice {
}
@ControllerAdvice
// @Order and @Priority should be ignored due to implementation of Ordered.
@ -297,45 +291,59 @@ class ControllerAdviceBeanTests {
@@ -297,45 +291,59 @@ class ControllerAdviceBeanTests {
}
@ControllerAdvice ( annotations = ControllerAnnotation . class )
static class AnnotationSupport { }
static class AnnotationSupport {
}
@ControllerAdvice ( basePackageClasses = MarkerClass . class )
static class MarkerClassSupport { }
static class MarkerClassSupport {
}
@ControllerAdvice ( assignableTypes = { ControllerInterface . class ,
AbstractController . class } )
static class AssignableTypesSupport { }
static class AssignableTypesSupport {
}
@ControllerAdvice ( basePackages = "org.springframework.web.method" )
static class BasePackageSupport { }
static class BasePackageSupport {
}
@ControllerAdvice ( "org.springframework.web.method" )
static class BasePackageValueSupport { }
static class BasePackageValueSupport {
}
@ControllerAdvice ( annotations = ControllerAnnotation . class , assignableTypes = ControllerInterface . class )
static class MultipleSelectorsSupport { }
static class MultipleSelectorsSupport {
}
@ControllerAdvice ( basePackages = "java.util" , annotations = { RestController . class } )
static class ShouldNotMatch { }
static class ShouldNotMatch {
}
// Support classes
static class MarkerClass { }
static class MarkerClass {
}
@Retention ( RetentionPolicy . RUNTIME )
@interface ControllerAnnotation { }
@interface ControllerAnnotation {
}
@ControllerAnnotation
public static class AnnotatedController { }
public static class AnnotatedController {
}
interface ControllerInterface { }
interface ControllerInterface {
}
static class ImplementationController implements ControllerInterface { }
static class ImplementationController implements ControllerInterface {
}
abstract static class AbstractController { }
abstract static class AbstractController {
}
static class InheritanceController extends AbstractController { }
static class InheritanceController extends AbstractController {
}
@Configuration ( proxyBeanMethods = false )
static class Config {