Skip to content

Commit

Permalink
Rename BeanOverrideStrategy enum constants
Browse files Browse the repository at this point in the history
Closes gh-33701
  • Loading branch information
sbrannen committed Oct 15, 2024
1 parent 7ea43bb commit 67cb3c7
Show file tree
Hide file tree
Showing 22 changed files with 97 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@

`@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
latter case, the original bean definition is not replaced, but instead an early instance
of the bean is captured and wrapped by the spy.
latter case, an early instance of the original bean is captured and wrapped by the spy.

By default, the annotated field's type is used to search for candidate bean definitions
to override. If multiple candidates match, `@Qualifier` can be provided to narrow the
candidate to override. Alternatively, a candidate whose bean definition name matches the
name of the field will match.
By default, the annotated field's type is used to search for candidate beans to override.
If multiple candidates match, `@Qualifier` can be provided to narrow the candidate to
override. Alternatively, a candidate whose bean name matches the name of the field will
match.

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)`.
When using `@MockitoBean`, a new bean will be created if a corresponding bean does not
exist. However, if you would like for the test to fail when a corresponding bean 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 All @@ -32,14 +31,16 @@ During the test class lifecycle, Mockito is set up via the `Mockito#mockitoSessi
mechanism. Notably, it enables `STRICT_STUBS` mode by default. This can be changed on
individual test classes with the `@MockitoBeanSettings` annotation.

The `@MockitoBean` annotation uses the `REPLACE_OR_CREATE_DEFINITION`
By default, the `@MockitoBean` annotation uses the `REPLACE_OR_CREATE`
xref:testing/testcontext-framework/bean-overriding.adoc#testcontext-bean-overriding-custom[strategy for test bean overriding].
If no existing bean definition matches, a new bean definition is created on the fly.
If no existing bean matches, a new bean is created on the fly. As mentioned previously,
you can switch to the `REPLACE` strategy by setting the `enforceOverride` attribute to
`true`.

The `@MockitoSpyBean` annotation uses the `WRAP_BEAN`
The `@MockitoSpyBean` annotation uses the `WRAP`
xref:testing/testcontext-framework/bean-overriding.adoc#testcontext-bean-overriding-custom[strategy],
and the original instance is wrapped in a Mockito spy. This strategy requires that
exactly one candidate bean definition exists.
exactly one candidate bean exists.

NOTE: Only _singleton_ beans can be overridden. Any attempt to override a non-singleton
bean will result in an exception.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,14 @@ have a return type compatible with the type of the bean to override. To make thi
explicit, or if you'd rather use a different name, the annotation allows for a specific
method name to be provided.

By default, the annotated field's type is used to search for candidate bean definitions
to override. If multiple candidates match, `@Qualifier` can be provided to narrow the
candidate to override. Alternatively, a candidate whose bean definition name matches the
name of the field will match.

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)`.
By default, the annotated field's type is used to search for candidate beans to override.
If multiple candidates match, `@Qualifier` can be provided to narrow the candidate to
override. Alternatively, a candidate whose bean name matches the name of the field will
match.

A bean will be created if a corresponding bean does not exist. However, if you would like
for the test to fail when a corresponding bean 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 @@ -46,14 +46,16 @@ with `@BeanOverride` and instantiates the corresponding `BeanOverrideProcessor`
responsible for registering appropriate `OverrideMetadata`.

The internal `BeanOverrideBeanFactoryPostProcessor` then uses that information to alter
the test's `ApplicationContext` by registering and replacing bean definitions as defined
by the corresponding `BeanOverrideStrategy`:

* `REPLACE_DEFINITION`: Replaces the bean definition. Throws an exception if a
corresponding bean definition does not exist.
* `REPLACE_OR_CREATE_DEFINITION`: Replaces the bean definition if it exists. Creates a
new bean definition if a corresponding bean definition does not exist.
* `WRAP_BEAN`: Retrieves the original bean instance and wraps it.
the test's `ApplicationContext` by registering and replacing beans as defined by the
corresponding `BeanOverrideStrategy`:

`REPLACE`::
Replaces the bean. Throws an exception if a corresponding bean does not exist.
`REPLACE_OR_CREATE`::
Replaces the bean if it exists. Creates a new bean if a corresponding bean does not
exist.
`WRAP`::
Retrieves the original bean and wraps it.

[NOTE]
====
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,14 @@ private void registerBeanOverride(ConfigurableListableBeanFactory beanFactory, O
beanName, field.getDeclaringClass().getSimpleName(), field.getName()));

switch (overrideMetadata.getStrategy()) {
case REPLACE_DEFINITION -> replaceDefinition(beanFactory, overrideMetadata, true);
case REPLACE_OR_CREATE_DEFINITION -> replaceDefinition(beanFactory, overrideMetadata, false);
case WRAP_BEAN -> wrapBean(beanFactory, overrideMetadata);
case REPLACE -> replaceBean(beanFactory, overrideMetadata, true);
case REPLACE_OR_CREATE -> replaceBean(beanFactory, overrideMetadata, false);
case WRAP -> wrapBean(beanFactory, overrideMetadata);
}
}

private void replaceDefinition(ConfigurableListableBeanFactory beanFactory, OverrideMetadata overrideMetadata,
boolean requireExistingDefinition) {
private void replaceBean(ConfigurableListableBeanFactory beanFactory, OverrideMetadata overrideMetadata,
boolean requireExistingBean) {

// NOTE: This method supports 3 distinct scenarios which must be accounted for.
//
Expand All @@ -124,7 +124,7 @@ private void replaceDefinition(ConfigurableListableBeanFactory beanFactory, Over
String beanName = overrideMetadata.getBeanName();
BeanDefinition existingBeanDefinition = null;
if (beanName == null) {
beanName = getBeanNameForType(beanFactory, overrideMetadata, requireExistingDefinition);
beanName = getBeanNameForType(beanFactory, overrideMetadata, requireExistingBean);
if (beanName != null) {
// We are overriding an existing bean by-type.
beanName = BeanFactoryUtils.transformedBeanName(beanName);
Expand All @@ -146,9 +146,9 @@ private void replaceDefinition(ConfigurableListableBeanFactory beanFactory, Over
// We are overriding an existing bean by-name.
existingBeanDefinition = beanFactory.getBeanDefinition(beanName);
}
else if (requireExistingDefinition) {
else if (requireExistingBean) {
throw new IllegalStateException("""
Unable to override bean: there is no bean definition to replace \
Unable to override bean: there is no bean to replace \
with name [%s] and type [%s]."""
.formatted(beanName, overrideMetadata.getBeanType()));
}
Expand Down Expand Up @@ -237,7 +237,7 @@ private void wrapBean(ConfigurableListableBeanFactory beanFactory, OverrideMetad
Set<String> candidates = getExistingBeanNamesByType(beanFactory, overrideMetadata, false);
if (!candidates.contains(beanName)) {
throw new IllegalStateException("""
Unable to override bean by wrapping: there is no existing bean definition \
Unable to override bean by wrapping: there is no existing bean \
with name [%s] and type [%s]."""
.formatted(beanName, overrideMetadata.getBeanType()));
}
Expand All @@ -249,26 +249,26 @@ private void wrapBean(ConfigurableListableBeanFactory beanFactory, OverrideMetad

@Nullable
private String getBeanNameForType(ConfigurableListableBeanFactory beanFactory, OverrideMetadata overrideMetadata,
boolean requireExistingDefinition) {
boolean requireExistingBean) {

Set<String> candidateNames = getExistingBeanNamesByType(beanFactory, overrideMetadata, true);
int candidateCount = candidateNames.size();
if (candidateCount == 1) {
return candidateNames.iterator().next();
}
else if (candidateCount == 0) {
if (requireExistingDefinition) {
if (requireExistingBean) {
Field field = overrideMetadata.getField();
throw new IllegalStateException(
"Unable to override bean: no bean definitions of type %s (as required by annotated field '%s.%s')"
"Unable to override bean: no beans of type %s (as required by annotated field '%s.%s')"
.formatted(overrideMetadata.getBeanType(), field.getDeclaringClass().getSimpleName(), field.getName()));
}
return null;
}

Field field = overrideMetadata.getField();
throw new IllegalStateException("""
Unable to select a bean definition to override: found %s bean definitions of type %s \
Unable to select a bean to override: found %s beans of type %s \
(as required by annotated field '%s.%s'): %s"""
.formatted(candidateCount, overrideMetadata.getBeanType(), field.getDeclaringClass().getSimpleName(),
field.getName(), candidateNames));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class BeanOverrideRegistrar {
*/
Object wrapIfNecessary(Object bean, String beanName) throws BeansException {
OverrideMetadata metadata = this.earlyOverrideMetadata.get(beanName);
if (metadata != null && metadata.getStrategy() == BeanOverrideStrategy.WRAP_BEAN) {
if (metadata != null && metadata.getStrategy() == BeanOverrideStrategy.WRAP) {
bean = metadata.createOverride(beanName, null, bean);
metadata.track(bean, this.beanFactory);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,24 @@
public enum BeanOverrideStrategy {

/**
* Replace a given bean definition, immediately preparing a singleton instance.
* <p>Fails if the original bean definition does not exist. To create a new bean
* definition in such a case, use {@link #REPLACE_OR_CREATE_DEFINITION} instead.
* Replace a given bean, immediately preparing a singleton instance.
* <p>Fails if the original bean does not exist. To create a new bean
* in such a case, use {@link #REPLACE_OR_CREATE} instead.
*/
REPLACE_DEFINITION,
REPLACE,

/**
* Replace or create a given bean definition, immediately preparing a
* singleton instance.
* <p>Contrary to {@link #REPLACE_DEFINITION}, this creates a new bean
* definition if the target bean definition does not exist rather than
* failing.
* Replace or create a given bean, immediately preparing a singleton instance.
* <p>Contrary to {@link #REPLACE}, this strategy creates a new bean if the
* target bean does not exist rather than failing.
*/
REPLACE_OR_CREATE_DEFINITION,
REPLACE_OR_CREATE,

/**
* Intercept and process an early bean reference rather than a bean
* definition, allowing variants of bean overriding to wrap the instance
* &mdash; for example, to delegate to actual methods in the context of a
* mocking "spy".
* Intercept and process an early bean reference, allowing variants of bean
* overriding to wrap the original bean instance &mdash; for example, to
* delegate to actual methods in the context of a mocking "spy".
*/
WRAP_BEAN
WRAP

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@

/**
* {@link SmartInstantiationAwareBeanPostProcessor} implementation that wraps
* beans in order to support the {@link BeanOverrideStrategy#WRAP_BEAN WRAP_BEAN}
* bean override strategy.
* beans in order to support the {@link BeanOverrideStrategy#WRAP WRAP} bean
* override strategy.
*
* @author Simon Baslé
* @author Stephane Nicoll
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@
* 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_OR_CREATE_DEFINITION
* @see org.springframework.test.context.bean.override.BeanOverrideStrategy#REPLACE_DEFINITION
* @see org.springframework.test.context.bean.override.BeanOverrideStrategy#REPLACE_OR_CREATE
* @see org.springframework.test.context.bean.override.BeanOverrideStrategy#REPLACE
*/
boolean enforceOverride() default false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodFilter;

import static org.springframework.test.context.bean.override.BeanOverrideStrategy.REPLACE_DEFINITION;
import static org.springframework.test.context.bean.override.BeanOverrideStrategy.REPLACE_OR_CREATE_DEFINITION;
import static org.springframework.test.context.bean.override.BeanOverrideStrategy.REPLACE;
import static org.springframework.test.context.bean.override.BeanOverrideStrategy.REPLACE_OR_CREATE;

/**
* {@link BeanOverrideProcessor} implementation for {@link TestBean @TestBean}
Expand All @@ -62,7 +62,7 @@ public TestBeanOverrideMetadata createMetadata(Annotation overrideAnnotation, Cl

String beanName = (!testBean.name().isBlank() ? testBean.name() : null);
String methodName = testBean.methodName();
BeanOverrideStrategy strategy = (testBean.enforceOverride() ? REPLACE_DEFINITION : REPLACE_OR_CREATE_DEFINITION);
BeanOverrideStrategy strategy = (testBean.enforceOverride() ? REPLACE : REPLACE_OR_CREATE);

Method overrideMethod;
if (!methodName.isBlank()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@
* 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_OR_CREATE_DEFINITION
* @see org.springframework.test.context.bean.override.BeanOverrideStrategy#REPLACE_DEFINITION
* @see org.springframework.test.context.bean.override.BeanOverrideStrategy#REPLACE_OR_CREATE
* @see org.springframework.test.context.bean.override.BeanOverrideStrategy#REPLACE
*/
boolean enforceOverride() default false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import static org.springframework.test.context.bean.override.BeanOverrideStrategy.REPLACE_DEFINITION;
import static org.springframework.test.context.bean.override.BeanOverrideStrategy.REPLACE_OR_CREATE_DEFINITION;
import static org.springframework.test.context.bean.override.BeanOverrideStrategy.REPLACE;
import static org.springframework.test.context.bean.override.BeanOverrideStrategy.REPLACE_OR_CREATE;

/**
* {@link OverrideMetadata} implementation for Mockito {@code mock} support.
Expand All @@ -59,7 +59,7 @@ class MockitoBeanOverrideMetadata extends AbstractMockitoOverrideMetadata {

MockitoBeanOverrideMetadata(Field field, ResolvableType typeToMock, MockitoBean mockitoBean) {
this(field, typeToMock, (!mockitoBean.name().isBlank() ? mockitoBean.name() : null),
(mockitoBean.enforceOverride() ? REPLACE_DEFINITION : REPLACE_OR_CREATE_DEFINITION),
(mockitoBean.enforceOverride() ? REPLACE : REPLACE_OR_CREATE),
mockitoBean.reset(), mockitoBean.extraInterfaces(), mockitoBean.answers(), mockitoBean.serializable());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class MockitoSpyBeanOverrideMetadata extends AbstractMockitoOverrideMetadata {
MockitoSpyBeanOverrideMetadata(Field field, ResolvableType typeToSpy, @Nullable String beanName,
MockReset reset, boolean proxyTargetAware) {

super(field, typeToSpy, beanName, BeanOverrideStrategy.WRAP_BEAN, reset, proxyTargetAware);
super(field, typeToSpy, beanName, BeanOverrideStrategy.WRAP, reset, proxyTargetAware);
Assert.notNull(typeToSpy, "typeToSpy must not be null");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ void replaceBeanByNameWithoutMatchingBeanDefinitionFails() {

assertThatIllegalStateException()
.isThrownBy(context::refresh)
.withMessage("Unable to override bean: there is no bean definition " +
.withMessage("Unable to override bean: there is no bean " +
"to replace with name [descriptionBean] and type [java.lang.String].");
}

Expand All @@ -94,7 +94,7 @@ void replaceBeanByNameWithMatchingBeanDefinitionAndWrongTypeFails() {

assertThatIllegalStateException()
.isThrownBy(context::refresh)
.withMessage("Unable to override bean: there is no bean definition " +
.withMessage("Unable to override bean: there is no bean " +
"to replace with name [descriptionBean] and type [java.lang.String].");
}

Expand Down Expand Up @@ -140,7 +140,7 @@ void replaceBeanByTypeWithoutMatchingBeanFails() {

assertThatIllegalStateException()
.isThrownBy(context::refresh)
.withMessage("Unable to override bean: no bean definitions of type java.lang.Integer " +
.withMessage("Unable to override bean: no beans of type java.lang.Integer " +
"(as required by annotated field 'CaseByType.counter')");
}

Expand All @@ -152,7 +152,7 @@ void replaceBeanByTypeWithMultipleMatchesAndNoQualifierFails() {

assertThatIllegalStateException()
.isThrownBy(context::refresh)
.withMessage("Unable to select a bean definition to override: found 2 bean definitions " +
.withMessage("Unable to select a bean to override: found 2 beans " +
"of type java.lang.Integer (as required by annotated field 'CaseByType.counter'): " +
"[someInteger, anotherInteger]");
}
Expand Down Expand Up @@ -388,24 +388,24 @@ static class CaseByType {

static class CaseByNameWithReplaceOrCreateStrategy {

@DummyBean(beanName = "descriptionBean", strategy = BeanOverrideStrategy.REPLACE_OR_CREATE_DEFINITION)
@DummyBean(beanName = "descriptionBean", strategy = BeanOverrideStrategy.REPLACE_OR_CREATE)
private String description;

}

static class CaseByTypeWithReplaceOrCreateStrategy {

@DummyBean(strategy = BeanOverrideStrategy.REPLACE_OR_CREATE_DEFINITION)
@DummyBean(strategy = BeanOverrideStrategy.REPLACE_OR_CREATE)
private String description;

}

static class CaseByNameAndByTypeWithReplaceOrCreateStrategy {

@DummyBean(beanName = "descriptionBean", strategy = BeanOverrideStrategy.REPLACE_OR_CREATE_DEFINITION)
@DummyBean(beanName = "descriptionBean", strategy = BeanOverrideStrategy.REPLACE_OR_CREATE)
private String description;

@DummyBean(strategy = BeanOverrideStrategy.REPLACE_OR_CREATE_DEFINITION)
@DummyBean(strategy = BeanOverrideStrategy.REPLACE_OR_CREATE)
private Integer counter;

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ void createContextCustomizerWhenNestedTestHasBeanOverrideAsWellAsTheParent() {


private Consumer<OverrideMetadata> dummyMetadata(@Nullable String beanName, Class<?> beanType) {
return dummyMetadata(beanName, beanType, BeanOverrideStrategy.REPLACE_DEFINITION);
return dummyMetadata(beanName, beanType, BeanOverrideStrategy.REPLACE);
}

private Consumer<OverrideMetadata> dummyMetadata(@Nullable String beanName, Class<?> beanType, BeanOverrideStrategy strategy) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ private static class DummyOverrideMetadata extends OverrideMetadata {

public DummyOverrideMetadata(String key) {
super(ReflectionUtils.findField(DummyOverrideMetadata.class, "key"),
ResolvableType.forClass(Object.class), null, BeanOverrideStrategy.REPLACE_DEFINITION);
ResolvableType.forClass(Object.class), null, BeanOverrideStrategy.REPLACE);
this.key = key;
}

Expand Down
Loading

0 comments on commit 67cb3c7

Please sign in to comment.