Skip to content

Commit

Permalink
Change enforceOverride flag to false in @⁠TestBean and @⁠MockitoBean
Browse files Browse the repository at this point in the history
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
  • Loading branch information
sbrannen committed Oct 9, 2024
1 parent 381b16f commit 1afcb22
Show file tree
Hide file tree
Showing 12 changed files with 100 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,17 @@
* {@link org.springframework.context.ApplicationContext ApplicationContext}
* using a static factory method.
*
* <p>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
* <p>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.
*
* <p>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}.
*
* <p>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
Expand Down Expand Up @@ -149,13 +150,13 @@
/**
* Whether to require the existence of a bean definition for the bean being
* overridden.
* <p>Defaults to {@code true} which means that an exception will be thrown
* if a corresponding bean definition does not exist.
* <p>Set to {@code false} to create a new bean definition when a corresponding
* <p>Defaults to {@code false} which means that a new bean definition will
* be created if a corresponding bean definition does not exist.
* <p>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;

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,17 @@
* {@link org.springframework.context.ApplicationContext ApplicationContext}
* using a Mockito mock.
*
* <p>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}.
* <p>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.
*
* <p>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}.
*
* <p>Dependencies that are known to the application context but are not beans
* (such as those
Expand Down Expand Up @@ -105,13 +108,13 @@
/**
* Whether to require the existence of a bean definition for the bean being
* overridden.
* <p>Defaults to {@code true} which means that an exception will be thrown
* if a corresponding bean definition does not exist.
* <p>Set to {@code false} to create a new bean definition when a corresponding
* <p>Defaults to {@code false} which means that a new bean definition will
* be created if a corresponding bean definition does not exist.
* <p>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;

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class MockitoBeanJupiterTests {
/**
* Mock for nonexistent bean.
*/
@MockitoBean(enforceOverride = false)
@MockitoBean
GreetingService greetingService;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
@SpringJUnitConfig
public class TestBeanForByTypeLookupIntegrationTests {

@TestBean(enforceOverride = false)
@TestBean
MessageService messageService;

@TestBean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ void contextCustomizerCannotBeCreatedWitCompetingOverrideMethods() {

static class FailureByTypeLookup {

@TestBean
@TestBean(enforceOverride = true)
private String example;

static String example() {
Expand All @@ -136,7 +136,7 @@ static String example() {

static class FailureByNameLookup {

@TestBean(name = "beanToOverride")
@TestBean(name = "beanToOverride", enforceOverride = true)
private String example;

static String example() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@
@SpringJUnitConfig
public class MockitoBeanForByTypeLookupIntegrationTests {

@MockitoBean(enforceOverride = false)
@MockitoBean
AnotherService serviceIsNotABean;

@MockitoBean(enforceOverride = false)
@MockitoBean
ExampleService anyNameForService;

@MockitoBean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ static class ExplicitStrictness extends BaseCase {
@DirtiesContext
static class ImplicitStrictnessWithMockitoBean extends BaseCase {

@MockitoBean(enforceOverride = false)
@MockitoBean
@SuppressWarnings("unused")
DateTimeFormatter ignoredMock;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

}
Expand Down

0 comments on commit 1afcb22

Please sign in to comment.