Support JUnit Jupiter ExtensionContextScope.TEST_METHOD

Historically, @⁠Autowired fields in an enclosing class of a @⁠Nested
test class have been injected from the ApplicationContext for the
enclosing class. If the enclosing test class and @⁠Nested test class
share the same ApplicationContext configuration, things work as
developers expect. However, if the enclosing class and @⁠Nested test
class have different ApplicationContexts, that can lead to
difficult-to-debug scenarios. For example, a bean injected into the
enclosing test class will not participate in a test-managed transaction
in the @⁠Nested test class (see gh-34576).

JUnit Jupiter 5.12 introduced a new ExtensionContextScope feature which
allows the SpringExtension to behave the same for @⁠Autowired fields as
it already does for @⁠Autowired arguments in lifecycle and test
methods. Specifically, if a developer sets the ExtensionContextScope to
TEST_METHOD — for example, by configuring the following configuration
parameter as a JVM system property or in a `junit-platform.properties`
file — the SpringExtension already supports dependency injection from
the current, @⁠Nested ApplicationContext in @⁠Autowired fields in an
enclosing class of the @⁠Nested test class.

junit.jupiter.extensions.testinstantiation.extensioncontextscope.default=test_method

However, there are two scenarios that fail as of Spring Framework
6.2.12.

1. @⁠TestConstructor configuration in @⁠Nested class hierarchies.
2. Field injection for bean overrides (such as @⁠MockitoBean) in
   @⁠Nested class hierarchies.

Commit 82c34f7b51 fixed the SpringExtension to support scenario #2
above.

To fix scenario #1, this commit revises
BeanOverrideTestExecutionListener's injectField() implementation to
look up the fields to inject for the "current test instance" instead of
for the "current test class".

This commit also introduces tests for both scenarios.

See gh-34576
See gh-35676
Closes gh-35680
This commit is contained in:
Sam Brannen
2025-10-21 15:34:11 +02:00
parent a5141b187a
commit d24a31d469
21 changed files with 1859 additions and 43 deletions
@@ -93,9 +93,17 @@ public class BeanOverrideTestExecutionListener extends AbstractTestExecutionList
* a corresponding bean override instance.
*/
private static void injectFields(TestContext testContext) {
List<BeanOverrideHandler> handlers = BeanOverrideHandler.forTestClass(testContext.getTestClass());
Object testInstance = testContext.getTestInstance();
// Since JUnit Jupiter 5.12, if the SpringExtension is used with Jupiter's
// ExtensionContextScope.TEST_METHOD mode, the value returned from
// testContext.getTestClass() may refer to the declaring class of the test
// method which is about to be invoked (which may be in a @Nested class
// within the class for the test instance). Thus, we use the class for the
// test instance as the "test class".
Class<?> testClass = testInstance.getClass();
List<BeanOverrideHandler> handlers = BeanOverrideHandler.forTestClass(testClass);
if (!handlers.isEmpty()) {
Object testInstance = testContext.getTestInstance();
ApplicationContext applicationContext = testContext.getApplicationContext();
Assert.state(applicationContext.containsBean(BeanOverrideRegistry.BEAN_NAME), () -> """