|
This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Framework 7.0.8! |
Bean Overriding in Tests
Bean overriding in tests refers to the ability to override specific beans in the
ApplicationContext for a test class, by annotating the test class or one or more
non-static fields in the test class.
This feature is intended as a less risky alternative to the practice of registering
a bean via @Bean with the DefaultListableBeanFactory
setAllowBeanDefinitionOverriding flag set to true.
|
The Spring TestContext framework provides two sets of annotations for bean overriding.
The former relies purely on Spring, while the latter set relies on the Mockito third-party library.
Custom Bean Override Support
The three annotations mentioned above build upon the @BeanOverride meta-annotation and
associated infrastructure, which allows one to define custom bean overriding variants.
To implement custom bean override support, the following is needed:
-
An annotation meta-annotated with
@BeanOverridethat defines theBeanOverrideProcessorto use -
A custom
BeanOverrideProcessorimplementation -
One or more concrete
BeanOverrideHandlerimplementations created by the processor
The Spring TestContext framework includes implementations of the following APIs that support bean overriding and are responsible for setting up the rest of the infrastructure.
-
a
BeanFactoryPostProcessor -
a
ContextCustomizerFactory -
a
TestExecutionListener
The spring-test module registers implementations of the latter two
(BeanOverrideContextCustomizerFactory and BeanOverrideTestExecutionListener) in its
META-INF/spring.factories
properties file.
The bean overriding infrastructure searches for annotations on test classes as well as
annotations on non-static fields in test classes that are meta-annotated with
@BeanOverride and instantiates the corresponding BeanOverrideProcessor which is
responsible for creating an appropriate BeanOverrideHandler.
The internal BeanOverrideBeanFactoryPostProcessor then uses bean override handlers to
alter the test’s ApplicationContext by creating, replacing, or wrapping 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.
|
When replacing a non-singleton bean, the non-singleton bean will be replaced with a
singleton bean corresponding to bean override instance created by the applicable
When replacing a bean created by a When wrapping a bean created by a |
|
In contrast to Spring’s autowiring mechanism (for example, resolution of an Typically, the bean is selected "by type" by the
|
Bean Overrides and Spring AOP Proxies
Beans in a Spring ApplicationContext are frequently wrapped in an AOP proxy — for
example, to support @Transactional, @Cacheable, or @Retryable semantics. Whether an
overridden bean retains such a proxy depends on the BeanOverrideStrategy used to create
the override.
-
Overrides that use the
REPLACEorREPLACE_OR_CREATEstrategy (such as@TestBeanand@MockitoBean) register their override instance directly as a manual singleton, which bypasses the container’s normal bean post-processing. Consequently, the override instance is a bare object: none of the AOP advice that would otherwise apply to the original bean (@Transactional,@Cacheable,@Retryable, method security, and so on) is present. -
Overrides that use the
WRAPstrategy (such as@MockitoSpyBean) capture an early reference to the original bean and use it to create the override instance, before the rest of the container’s post-processors — including the one responsible for creating AOP proxies — have run. Consequently, if the original bean would have been proxied, that proxy is still created, but it now wraps the override instance instead of the original bean. The bean that ends up in theApplicationContext, and that is injected into collaborating beans and test classes, is therefore the AOP proxy, with the override instance as its target — not the bare override instance itself.
The following diagrams illustrate the resulting shape of the bean for each strategy, from the perspective of a caller invoking a method on the injected bean.
With the REPLACE or REPLACE_OR_CREATE strategy, there is no AOP proxy at all: the
caller invokes the override instance directly.
caller
│
▼
[ override instance ]
With the WRAP strategy, any AOP proxy that would normally have wrapped the original
bean is still created, but now wraps the override instance instead:
caller
│
▼
[ AOP proxy ] (for example, retry, caching, or transaction advice)
│
│ delegates to its target
▼
[ override instance ] (for example, a Mockito spy created by @MockitoSpyBean)
For a WRAP-based override such as @MockitoSpyBean, the "wrapping" performed by the
AOP proxy is unrelated to the manner in which the resulting Mockito spy itself "wraps"
the original bean instance it was created from. The proxy shown above determines which
object a caller actually invokes, whereas the spy’s relationship to the original
instance only determines what happens when an unstubbed method is invoked on the spy: it
falls through to that instance’s real behavior.
This distinction has practical consequences when combining bean overrides with Mockito’s
stubbing and verification APIs. See
@MockitoSpyBean
and Spring AOP Proxies for details.