Provide first-class support for Bean Overrides with @ContextHierarchy
#34723
+2,486
−42
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Background
As explained by @wilkinsona in #33293 (comment), the Spring TestContext Framework (TCF) creates a
MergedContextConfiguration
for eachApplicationContext
that it may create. Furthermore, this applies to everyApplicationContext
created for each level in a@ContextHierarchy
.Thus, by default, all context configuration mechanisms for a given test class will be applied to all application contexts created for that test class. Specifically, this means that features such as
@ActiveProfiles
,@TestPropertySource
, and bean overrides (@TestBean
,@MockitoBean
,@MockitoSpyBean
, etc.) will be applied to each application context.For features such as
@ActiveProfiles
and@TestPropertySource
, that makes sense. You probably always want the same bean definition profiles and test property sources to be applied to every application context in a context hierarchy.However, with bean overrides that is typically not the case. Rather, when you configure a bean override, you expect Spring to override a specific bean in a specific application context in the context hierarchy. Phrased differently, you probably do not want Spring to attempt to override matching beans in all application contexts in a context hierarchy.
If Spring does attempt to override matching beans in all application contexts in a context hierarchy, that can lead to unexpected behavior or errors like those reported in #33293 and #34597.
Proposal
This PR provides first-class support for Bean Overrides (
@MockitoBean
,@MockitoSpyBean
,@TestBean
, etc.) with@ContextHierarchy
.Specifically, bean overrides can now specify which
ApplicationContext
they target within the context hierarchy by configuring thecontextName
attribute in the annotation. ThecontextName
must match a correspondingname
configured via@ContextConfiguration
.For example, the following test class configures the name of the second hierarchy level to be "child" and simultaneously specifies that the
ExampleService
should be wrapped in a Mockito spy in the context named "child". Consequently, Spring will only attempt to create the spy in the "child" context and will not attempt to create the spy in the parent context.The above example demonstrates how this feature would help to resolve the issue reported in #34597.
A similar approach can be applied to the failing use case reported in #33293, and the
ReusedParentConfigV1Tests
andReusedParentConfigV2Tests
test classes in this PR demonstrate that.@Autowired
with Magical PowersThe proposed feature also introduces functionality that is not possible with
@Autowired
.@Autowired
will always inject a matching bean found in the lowest level of the context hierarchy. However, with bean overrides in different levels of the context hierarchy, you may need to be able to have all of those bean override instances injected into the test class in order to interact with them -- for example, to configure stubbing for a mock.The following example demonstrates that is possible to inject an
ExampleService
mock from both the "parent" and the "child" context simultaneously.Ramifications
In order to support reliable context caching and field injection, the
contextName
attribute contributes toMergedContextConfiguration
cache key. Thus, bean overrides that would otherwise appear to be logically equivalent are not considered equivalent if they specify different context names. Consequently, specifying different context names can result in an application context being loaded multiple times.Related Issues
ApplicationContext
reloaded when@MockitoBean
and@ContextHierarchy
are mixed on single class #33293@MockitoSpyBean
does not work in tests with@ContextHierarchy
#34597BeanOverrideTestExecutionListener
#34726