From 1afcb2220557f4362e55fafa7abcc5a4934f1450 Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Wed, 9 Oct 2024 17:08:58 +0200 Subject: [PATCH] =?UTF-8?q?Change=20enforceOverride=20flag=20to=20false=20?= =?UTF-8?q?in=20@=E2=81=A0TestBean=20and=20@=E2=81=A0MockitoBean?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on feedback from the Spring Boot team, we have decided to change the default values for the enforceOverride flags in @TestBean and @MockitoBean from true to false. Closes gh-33613 --- .../annotation-mockitobean.adoc | 9 +-- .../annotation-testbean.adoc | 7 ++- .../bean/override/convention/TestBean.java | 29 +++++----- .../bean/override/mockito/MockitoBean.java | 29 +++++----- .../override/MockitoBeanJupiterTests.java | 2 +- ...OverrideBeanFactoryPostProcessorTests.java | 2 +- ...stBeanForByTypeLookupIntegrationTests.java | 2 +- .../override/convention/TestBeanTests.java | 4 +- ...toBeanForByNameLookupIntegrationTests.java | 4 +- ...toBeanForByTypeLookupIntegrationTests.java | 4 +- ...itoBeanSettingsStrictIntegrationTests.java | 2 +- .../mockito/MockitoMockBeanTests.java | 56 +++++++++++++++++-- 12 files changed, 100 insertions(+), 50 deletions(-) diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-mockitobean.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-mockitobean.adoc index 483ca3b2d0ea..818b8cc23099 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-mockitobean.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-mockitobean.adoc @@ -2,7 +2,7 @@ = `@MockitoBean` and `@MockitoSpyBean` `@MockitoBean` and `@MockitoSpyBean` are used on fields in test classes to override beans -in the test's `ApplicationContext` with a Mockito mock or spy, respectively. In the +in the test's `ApplicationContext` with a Mockito _mock_ or _spy_, respectively. In the latter case, the original bean definition is not replaced, but instead an early instance of the bean is captured and wrapped by the spy. @@ -11,9 +11,10 @@ to override. If multiple candidates match, `@Qualifier` can be provided to narro candidate to override. Alternatively, a candidate whose bean definition name matches the name of the field will match. -When using `@MockitoBean`, if you would like for a new bean definition to be created when -a corresponding bean definition does not exist, set the `enforceOverride` attribute to -`false` – for example, `@MockitoBean(enforceOverride = false)`. +When using `@MockitoBean`, a new bean definition will be created if a corresponding bean +definition does not exist. However, if you would like for the test to fail when a +corresponding bean definition does not exist, you can set the `enforceOverride` attribute +to `true` – for example, `@MockitoBean(enforceOverride = true)`. To use a by-name override rather than a by-type override, specify the `name` attribute of the annotation. diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-testbean.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-testbean.adoc index a384ecbcb651..9ef336315d85 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-testbean.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-testbean.adoc @@ -15,9 +15,10 @@ to override. If multiple candidates match, `@Qualifier` can be provided to narro candidate to override. Alternatively, a candidate whose bean definition name matches the name of the field will match. -If you would like for a new bean definition to be created when a corresponding bean -definition does not exist, set the `enforceOverride` attribute to `false` – for example, -`@TestBean(enforceOverride = false)`. +A new bean definition will be created if a corresponding bean definition does not exist. +However, if you would like for the test to fail when a corresponding bean definition does +not exist, you can set the `enforceOverride` attribute to `true` – for example, +`@TestBean(enforceOverride = true)`. To use a by-name override rather than a by-type override, specify the `name` attribute of the annotation. diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBean.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBean.java index 051199b32ca0..0bd3d72f3ee9 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBean.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBean.java @@ -31,16 +31,17 @@ * {@link org.springframework.context.ApplicationContext ApplicationContext} * using a static factory method. * - *
By default, the bean to override is inferred from the type of the - * annotated field. This requires that exactly one matching bean definition is - * present in the application context. A {@code @Qualifier} annotation can be + *
By default, the bean to override is inferred from the type of the annotated + * field. If multiple candidates exist, a {@code @Qualifier} annotation can be * used to help disambiguate. In the absence of a {@code @Qualifier} annotation, - * the name of the annotated field will be used as a qualifier. Alternatively, - * you can explicitly specify a bean name to replace by setting the - * {@link #value() value} or {@link #name() name} attribute. If you would like - * for a new bean definition to be created when a corresponding bean definition - * does not exist, set the {@link #enforceOverride() enforceOverride} attribute - * to {@code false}. + * the name of the annotated field will be used as a fallback qualifier. + * Alternatively, you can explicitly specify a bean name to replace by setting the + * {@link #value() value} or {@link #name() name} attribute. + * + *
A new bean definition will be created if a corresponding bean definition does + * not exist. However, if you would like for the test to fail when a corresponding + * bean definition does not exist, you can set the {@link #enforceOverride() + * enforceOverride} attribute to {@code true}. * *
The instance is created from a zero-argument static factory method in the * test class whose return type is compatible with the annotated field. In the @@ -149,13 +150,13 @@ /** * Whether to require the existence of a bean definition for the bean being * overridden. - *
Defaults to {@code true} which means that an exception will be thrown - * if a corresponding bean definition does not exist. - *
Set to {@code false} to create a new bean definition when a corresponding + *
Defaults to {@code false} which means that a new bean definition will + * be created if a corresponding bean definition does not exist. + *
Set to {@code true} to cause an exception to be thrown if a corresponding * bean definition does not exist. - * @see org.springframework.test.context.bean.override.BeanOverrideStrategy#REPLACE_DEFINITION * @see org.springframework.test.context.bean.override.BeanOverrideStrategy#REPLACE_OR_CREATE_DEFINITION + * @see org.springframework.test.context.bean.override.BeanOverrideStrategy#REPLACE_DEFINITION */ - boolean enforceOverride() default true; + boolean enforceOverride() default false; } diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBean.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBean.java index 68b27b6bc636..76162a7c2c6a 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBean.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBean.java @@ -33,14 +33,17 @@ * {@link org.springframework.context.ApplicationContext ApplicationContext} * using a Mockito mock. * - *
If no explicit {@link #name() name} is specified, a target bean definition - * is selected according to the type of the annotated field, and there must be - * exactly one such candidate definition in the context. Otherwise, a {@code @Qualifier} - * annotation can be used to help disambiguate between multiple candidates. If a - * {@link #name() name} is specified, by default a corresponding bean definition - * must exist in the application context. If you would like for a new bean definition - * to be created when a corresponding bean definition does not exist, set the - * {@link #enforceOverride() enforceOverride} attribute to {@code false}. + *
By default, the bean to override is inferred from the type of the annotated + * field. If multiple candidates exist, a {@code @Qualifier} annotation can be + * used to help disambiguate. In the absence of a {@code @Qualifier} annotation, + * the name of the annotated field will be used as a fallback qualifier. + * Alternatively, you can explicitly specify a bean name to replace by setting the + * {@link #name() name} attribute. + * + *
A new bean definition will be created if a corresponding bean definition does + * not exist. However, if you would like for the test to fail when a corresponding + * bean definition does not exist, you can set the {@link #enforceOverride() + * enforceOverride} attribute to {@code true}. * *
Dependencies that are known to the application context but are not beans * (such as those @@ -105,13 +108,13 @@ /** * Whether to require the existence of a bean definition for the bean being * overridden. - *
Defaults to {@code true} which means that an exception will be thrown - * if a corresponding bean definition does not exist. - *
Set to {@code false} to create a new bean definition when a corresponding + *
Defaults to {@code false} which means that a new bean definition will + * be created if a corresponding bean definition does not exist. + *
Set to {@code true} to cause an exception to be thrown if a corresponding * bean definition does not exist. - * @see org.springframework.test.context.bean.override.BeanOverrideStrategy#REPLACE_DEFINITION * @see org.springframework.test.context.bean.override.BeanOverrideStrategy#REPLACE_OR_CREATE_DEFINITION + * @see org.springframework.test.context.bean.override.BeanOverrideStrategy#REPLACE_DEFINITION */ - boolean enforceOverride() default true; + boolean enforceOverride() default false; } diff --git a/spring-test/src/test/java/org/springframework/test/context/aot/samples/bean/override/MockitoBeanJupiterTests.java b/spring-test/src/test/java/org/springframework/test/context/aot/samples/bean/override/MockitoBeanJupiterTests.java index 9cd784a77553..f914c84c5d6b 100644 --- a/spring-test/src/test/java/org/springframework/test/context/aot/samples/bean/override/MockitoBeanJupiterTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/aot/samples/bean/override/MockitoBeanJupiterTests.java @@ -40,7 +40,7 @@ public class MockitoBeanJupiterTests { /** * Mock for nonexistent bean. */ - @MockitoBean(enforceOverride = false) + @MockitoBean GreetingService greetingService; /** diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessorTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessorTests.java index 8e2a1f1c2058..d2c2fbcd7188 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessorTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideBeanFactoryPostProcessorTests.java @@ -420,7 +420,7 @@ static class CaseOverrideBeanProducedByFactoryBean { static class CaseByNameWithQualifier { @Qualifier("preferThis") - @TestBean(name = "descriptionBean", enforceOverride = false) + @TestBean(name = "descriptionBean") private String description; static String descriptionBean() { diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanForByTypeLookupIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanForByTypeLookupIntegrationTests.java index 6f7e170ac834..d410c8a280ba 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanForByTypeLookupIntegrationTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanForByTypeLookupIntegrationTests.java @@ -39,7 +39,7 @@ @SpringJUnitConfig public class TestBeanForByTypeLookupIntegrationTests { - @TestBean(enforceOverride = false) + @TestBean MessageService messageService; @TestBean diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanTests.java index 781dedf92797..314ba52b99c6 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanTests.java @@ -126,7 +126,7 @@ void contextCustomizerCannotBeCreatedWitCompetingOverrideMethods() { static class FailureByTypeLookup { - @TestBean + @TestBean(enforceOverride = true) private String example; static String example() { @@ -136,7 +136,7 @@ static String example() { static class FailureByNameLookup { - @TestBean(name = "beanToOverride") + @TestBean(name = "beanToOverride", enforceOverride = true) private String example; static String example() { diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanForByNameLookupIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanForByNameLookupIntegrationTests.java index 0b0e4b5eed5a..d2d225309627 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanForByNameLookupIntegrationTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanForByNameLookupIntegrationTests.java @@ -48,10 +48,10 @@ public class MockitoBeanForByNameLookupIntegrationTests { @MockitoBean(name = "nestedField") ExampleService renamed2; - @MockitoBean(name = "nonExistingBean", enforceOverride = false) + @MockitoBean(name = "nonExistingBean") ExampleService nonExisting1; - @MockitoBean(name = "nestedNonExistingBean", enforceOverride = false) + @MockitoBean(name = "nestedNonExistingBean") ExampleService nonExisting2; diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanForByTypeLookupIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanForByTypeLookupIntegrationTests.java index 3cf66858118a..0836b4571806 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanForByTypeLookupIntegrationTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanForByTypeLookupIntegrationTests.java @@ -48,10 +48,10 @@ @SpringJUnitConfig public class MockitoBeanForByTypeLookupIntegrationTests { - @MockitoBean(enforceOverride = false) + @MockitoBean AnotherService serviceIsNotABean; - @MockitoBean(enforceOverride = false) + @MockitoBean ExampleService anyNameForService; @MockitoBean diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanSettingsStrictIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanSettingsStrictIntegrationTests.java index 583c7d4314fb..a54fbf3756be 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanSettingsStrictIntegrationTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanSettingsStrictIntegrationTests.java @@ -93,7 +93,7 @@ static class ExplicitStrictness extends BaseCase { @DirtiesContext static class ImplicitStrictnessWithMockitoBean extends BaseCase { - @MockitoBean(enforceOverride = false) + @MockitoBean @SuppressWarnings("unused") DateTimeFormatter ignoredMock; } diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoMockBeanTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoMockBeanTests.java index a6a905e6378b..524bc9fe0a73 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoMockBeanTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoMockBeanTests.java @@ -29,27 +29,71 @@ * Tests for {@link MockitoBean}. * * @author Stephane Nicoll + * @author Sam Brannen */ class MockitoMockBeanTests { @Test - void contextCustomizerCannotBeCreatedWithTooManyCandidates() { + void cannotOverrideBeanByNameWithNoSuchBeanName() { + GenericApplicationContext context = new GenericApplicationContext(); + context.registerBean("anotherBean", String.class, () -> "example"); + BeanOverrideContextCustomizerTestUtils.customizeApplicationContext(FailureByNameLookup.class, context); + assertThatIllegalStateException() + .isThrownBy(context::refresh) + .withMessage(""" + Unable to override bean: there is no bean definition \ + to replace with name [beanToOverride] and type [java.lang.String]."""); + } + + @Test + void cannotOverrideBeanByNameWithBeanOfWrongType() { + GenericApplicationContext context = new GenericApplicationContext(); + context.registerBean("beanToOverride", Integer.class, () -> 42); + BeanOverrideContextCustomizerTestUtils.customizeApplicationContext(FailureByNameLookup.class, context); + assertThatIllegalStateException() + .isThrownBy(context::refresh) + .withMessage(""" + Unable to override bean: there is no bean definition \ + to replace with name [beanToOverride] and type [java.lang.String]."""); + } + + @Test + void cannotOverrideBeanByTypeWithNoSuchBeanType() { + GenericApplicationContext context = new GenericApplicationContext(); + BeanOverrideContextCustomizerTestUtils.customizeApplicationContext(FailureByTypeLookup.class, context); + assertThatIllegalStateException() + .isThrownBy(context::refresh) + .withMessage(""" + Unable to override bean: no bean definitions of \ + type %s (as required by annotated field '%s.example')""".formatted( + String.class.getName(), FailureByTypeLookup.class.getSimpleName())); + } + + @Test + void cannotOverrideBeanByTypeWithTooManyBeansOfThatType() { GenericApplicationContext context = new GenericApplicationContext(); context.registerBean("bean1", String.class, () -> "example1"); context.registerBean("bean2", String.class, () -> "example2"); - BeanOverrideContextCustomizerTestUtils.customizeApplicationContext(ByTypeSingleLookup.class, context); + BeanOverrideContextCustomizerTestUtils.customizeApplicationContext(FailureByTypeLookup.class, context); assertThatIllegalStateException() .isThrownBy(context::refresh) .withMessage(""" Unable to select a bean definition to override: found 2 bean definitions \ - of type %s (as required by annotated field '%s.example'): %s""", - String.class.getName(), ByTypeSingleLookup.class.getSimpleName(), List.of("bean1", "bean2")); + of type %s (as required by annotated field '%s.example'): %s""".formatted( + String.class.getName(), FailureByTypeLookup.class.getSimpleName(), List.of("bean1", "bean2"))); } - static class ByTypeSingleLookup { + static class FailureByTypeLookup { + + @MockitoBean(enforceOverride = true) + String example; + + } + + static class FailureByNameLookup { - @MockitoBean + @MockitoBean(name = "beanToOverride", enforceOverride = true) String example; }