Provide first-class support for Bean Overrides with @ContextHierarchy#34723
Merged
sbrannen merged 1 commit intoApr 10, 2025
Merged
Conversation
0eea7ef to
52b74ad
Compare
2fdf133 to
1bdd3a9
Compare
This was referenced Apr 7, 2025
ApplicationContext reloaded when @MockitoBean and @ContextHierarchy are mixed on single class
#33293
Closed
7ed9bac to
22a25ad
Compare
This commit 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 the
`contextName` attribute in the annotation. The `contextName` must match
a corresponding `name` 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.
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class),
@ContextConfiguration(classes = Config2.class, name = "child")
})
class MockitoSpyBeanContextHierarchyTests {
@MockitoSpyBean(contextName = "child")
ExampleService service;
// ...
}
See spring-projectsgh-33293
See spring-projectsgh-34597
See spring-projectsgh-34726
Signed-off-by: Sam Brannen <104798+sbrannen@users.noreply.github.com>
22a25ad to
7f6b579
Compare
Member
Author
|
This feature will be available in the upcoming 6.2.6 release and is available for early testing now in 6.2.6 snapshots. Documentation and examples can be viewed here:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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
MergedContextConfigurationfor eachApplicationContextthat it may create. Furthermore, this applies to everyApplicationContextcreated 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
@ActiveProfilesand@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
ApplicationContextthey target within the context hierarchy by configuring thecontextNameattribute in the annotation. ThecontextNamemust match a correspondingnameconfigured via@ContextConfiguration.For example, the following test class configures the name of the second hierarchy level to be "child" and simultaneously specifies that the
ExampleServiceshould 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
ReusedParentConfigV1TestsandReusedParentConfigV2Teststest classes in this PR demonstrate that.@Autowiredwith Magical PowersThe proposed feature also introduces functionality that is not possible with
@Autowired.@Autowiredwill 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
ExampleServicemock from both the "parent" and the "child" context simultaneously.Ramifications
In order to support reliable context caching and field injection, the
contextNameattribute contributes toMergedContextConfigurationcache 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
ApplicationContextreloaded when@MockitoBeanand@ContextHierarchyare mixed on single class #33293@MockitoSpyBeandoes not work in tests with@ContextHierarchy#34597BeanOverrideTestExecutionListener#34726