Provide first-class support for Bean Overrides with @⁠ContextHierarchy

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 gh-33293
See gh-34597
See gh-34726
Closes gh-34723

Signed-off-by: Sam Brannen <104798+sbrannen@users.noreply.github.com>
This commit is contained in:
Sam Brannen
2025-04-10 14:46:50 +02:00
committed by GitHub
parent 3afd551174
commit c168e1c297
52 changed files with 2972 additions and 77 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,12 +16,13 @@
package org.springframework.test.context.bean.override;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import org.junit.jupiter.api.Test;
import org.springframework.lang.Nullable;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.bean.override.DummyBean.DummyBeanOverrideProcessor.DummyBeanOverrideHandler;
import static org.assertj.core.api.Assertions.assertThat;
@@ -92,7 +93,7 @@ class BeanOverrideContextCustomizerFactoryTests {
@Nullable
private BeanOverrideContextCustomizer createContextCustomizer(Class<?> testClass) {
return this.factory.createContextCustomizer(testClass, Collections.emptyList());
return this.factory.createContextCustomizer(testClass, List.of(new ContextConfigurationAttributes(testClass)));
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,10 +16,11 @@
package org.springframework.test.context.bean.override;
import java.util.Collections;
import java.util.List;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.lang.Nullable;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.MergedContextConfiguration;
@@ -44,7 +45,7 @@ public abstract class BeanOverrideContextCustomizerTestUtils {
*/
@Nullable
public static ContextCustomizer createContextCustomizer(Class<?> testClass) {
return factory.createContextCustomizer(testClass, Collections.emptyList());
return factory.createContextCustomizer(testClass, List.of(new ContextConfigurationAttributes(testClass)));
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -72,7 +72,7 @@ class BeanOverrideContextCustomizerTests {
public DummyBeanOverrideHandler(String key) {
super(ReflectionUtils.findField(DummyBeanOverrideHandler.class, "key"),
ResolvableType.forClass(Object.class), null, BeanOverrideStrategy.REPLACE);
ResolvableType.forClass(Object.class), null, "", BeanOverrideStrategy.REPLACE);
this.key = key;
}

View File

@@ -30,6 +30,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.lang.Nullable;
import org.springframework.test.context.bean.override.DummyBean.DummyBeanOverrideProcessor;
import org.springframework.test.context.bean.override.DummyBean.DummyBeanOverrideProcessor.DummyBeanOverrideHandler;
import org.springframework.test.context.bean.override.example.CustomQualifier;
import org.springframework.test.context.bean.override.example.ExampleService;
@@ -116,7 +117,7 @@ class BeanOverrideHandlerTests {
}
@Test
void isEqualToWithSameMetadataAndBeanNames() {
void isEqualToWithSameMetadataAndSameBeanNames() {
BeanOverrideHandler handler1 = createBeanOverrideHandler(field(ConfigA.class, "noQualifier"), "testBean");
BeanOverrideHandler handler2 = createBeanOverrideHandler(field(ConfigA.class, "noQualifier"), "testBean");
assertThat(handler1).isEqualTo(handler2);
@@ -124,10 +125,29 @@ class BeanOverrideHandlerTests {
}
@Test
void isNotEqualToWithSameMetadataAndDifferentBeaName() {
void isNotEqualToWithSameMetadataButDifferentBeanNames() {
BeanOverrideHandler handler1 = createBeanOverrideHandler(field(ConfigA.class, "noQualifier"), "testBean");
BeanOverrideHandler handler2 = createBeanOverrideHandler(field(ConfigA.class, "noQualifier"), "testBean2");
assertThat(handler1).isNotEqualTo(handler2);
assertThat(handler1).doesNotHaveSameHashCodeAs(handler2);
}
@Test
void isEqualToWithSameMetadataSameBeanNamesAndSameContextNames() {
Class<?> testClass = MultipleAnnotationsWithSameNameInDifferentContext.class;
BeanOverrideHandler handler1 = createBeanOverrideHandler(testClass, field(testClass, "parentMessageBean"));
BeanOverrideHandler handler2 = createBeanOverrideHandler(testClass, field(testClass, "parentMessageBean2"));
assertThat(handler1).isEqualTo(handler2);
assertThat(handler1).hasSameHashCodeAs(handler2);
}
@Test
void isEqualToWithSameMetadataAndSameBeanNamesButDifferentContextNames() {
Class<?> testClass = MultipleAnnotationsWithSameNameInDifferentContext.class;
BeanOverrideHandler handler1 = createBeanOverrideHandler(testClass, field(testClass, "parentMessageBean"));
BeanOverrideHandler handler2 = createBeanOverrideHandler(testClass, field(testClass, "childMessageBean"));
assertThat(handler1).isNotEqualTo(handler2);
assertThat(handler1).doesNotHaveSameHashCodeAs(handler2);
}
@Test
@@ -173,6 +193,7 @@ class BeanOverrideHandlerTests {
BeanOverrideHandler handler1 = createBeanOverrideHandler(field(ConfigA.class, "directQualifier"));
BeanOverrideHandler handler2 = createBeanOverrideHandler(field(ConfigA.class, "differentDirectQualifier"));
assertThat(handler1).isNotEqualTo(handler2);
assertThat(handler1).doesNotHaveSameHashCodeAs(handler2);
}
@Test
@@ -180,6 +201,7 @@ class BeanOverrideHandlerTests {
BeanOverrideHandler handler1 = createBeanOverrideHandler(field(ConfigA.class, "directQualifier"));
BeanOverrideHandler handler2 = createBeanOverrideHandler(field(ConfigA.class, "customQualifier"));
assertThat(handler1).isNotEqualTo(handler2);
assertThat(handler1).doesNotHaveSameHashCodeAs(handler2);
}
@Test
@@ -187,6 +209,7 @@ class BeanOverrideHandlerTests {
BeanOverrideHandler handler1 = createBeanOverrideHandler(field(ConfigA.class, "noQualifier"));
BeanOverrideHandler handler2 = createBeanOverrideHandler(field(ConfigB.class, "example"));
assertThat(handler1).isNotEqualTo(handler2);
assertThat(handler1).doesNotHaveSameHashCodeAs(handler2);
}
private static BeanOverrideHandler createBeanOverrideHandler(Field field) {
@@ -194,7 +217,11 @@ class BeanOverrideHandlerTests {
}
private static BeanOverrideHandler createBeanOverrideHandler(Field field, @Nullable String name) {
return new DummyBeanOverrideHandler(field, field.getType(), name, BeanOverrideStrategy.REPLACE);
return new DummyBeanOverrideHandler(field, field.getType(), name, "", BeanOverrideStrategy.REPLACE);
}
private static BeanOverrideHandler createBeanOverrideHandler(Class<?> testClass, Field field) {
return new DummyBeanOverrideProcessor().createHandler(field.getAnnotation(DummyBean.class), testClass, field);
}
private static Field field(Class<?> target, String fieldName) {
@@ -234,6 +261,18 @@ class BeanOverrideHandlerTests {
Integer counter;
}
static class MultipleAnnotationsWithSameNameInDifferentContext {
@DummyBean(beanName = "messageBean", contextName = "parent")
String parentMessageBean;
@DummyBean(beanName = "messageBean", contextName = "parent")
String parentMessageBean2;
@DummyBean(beanName = "messageBean", contextName = "child")
String childMessageBean;
}
static class MultipleAnnotationsDuplicate {
@DummyBean(beanName = "messageBean")

View File

@@ -0,0 +1,143 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.platform.testkit.engine.EngineTestKit;
import org.junit.platform.testkit.engine.Events;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
import static org.junit.platform.testkit.engine.EventConditions.event;
import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;
import static org.junit.platform.testkit.engine.EventConditions.test;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;
import static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;
/**
* Integration tests for {@link BeanOverrideTestExecutionListener}.
*
* @author Sam Brannen
* @since 6.2.6
*/
class BeanOverrideTestExecutionListenerTests {
@Test
void beanOverrideWithNoMatchingContextName() {
executeTests(BeanOverrideWithNoMatchingContextNameTestCase.class)
.assertThatEvents().haveExactly(1, event(test("test"),
finishedWithFailure(
instanceOf(IllegalStateException.class),
message("""
Test class BeanOverrideWithNoMatchingContextNameTestCase declares @BeanOverride \
fields [message, number], but no BeanOverrideHandler has been registered. \
If you are using @ContextHierarchy, ensure that context names for bean overrides match \
configured @ContextConfiguration names."""))));
}
@Test
void beanOverrideWithInvalidContextName() {
executeTests(BeanOverrideWithInvalidContextNameTestCase.class)
.assertThatEvents().haveExactly(1, event(test("test"),
finishedWithFailure(
instanceOf(IllegalStateException.class),
message(msg ->
msg.startsWith("No bean override instance found for BeanOverrideHandler") &&
msg.contains("DummyBeanOverrideHandler") &&
msg.contains("BeanOverrideWithInvalidContextNameTestCase.message2") &&
msg.contains("contextName = 'BOGUS'") &&
msg.endsWith("""
If you are using @ContextHierarchy, ensure that context names for bean overrides match \
configured @ContextConfiguration names.""")))));
}
private static Events executeTests(Class<?> testClass) {
return EngineTestKit.engine("junit-jupiter")
.selectors(selectClass(testClass))
.execute()
.testEvents()
.assertStatistics(stats -> stats.started(1).failed(1));
}
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class),
@ContextConfiguration(classes = Config2.class, name = "child")
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
static class BeanOverrideWithNoMatchingContextNameTestCase {
@DummyBean(contextName = "BOGUS")
String message;
@DummyBean(contextName = "BOGUS")
Integer number;
@Test
void test() {
// no-op
}
}
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class),
@ContextConfiguration(classes = Config2.class, name = "child")
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
static class BeanOverrideWithInvalidContextNameTestCase {
@DummyBean(contextName = "child")
String message1;
@DummyBean(contextName = "BOGUS")
String message2;
@Test
void test() {
// no-op
}
}
@Configuration
static class Config1 {
@Bean
String message() {
return "Message 1";
}
}
@Configuration
static class Config2 {
@Bean
String message() {
return "Message 2";
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@ import org.springframework.util.StringUtils;
/**
* A dummy {@link BeanOverride} implementation that only handles {@link CharSequence}
* and {@link Integer} and replace them with {@code "overridden"} and {@code 42},
* and {@link Integer} and replaces them with {@code "overridden"} and {@code 42},
* respectively.
*
* @author Stephane Nicoll
@@ -45,6 +45,8 @@ import org.springframework.util.StringUtils;
String beanName() default "";
String contextName() default "";
BeanOverrideStrategy strategy() default BeanOverrideStrategy.REPLACE;
class DummyBeanOverrideProcessor implements BeanOverrideProcessor {
@@ -54,7 +56,7 @@ import org.springframework.util.StringUtils;
DummyBean dummyBean = (DummyBean) annotation;
String beanName = (StringUtils.hasText(dummyBean.beanName()) ? dummyBean.beanName() : null);
return new DummyBeanOverrideProcessor.DummyBeanOverrideHandler(field, field.getType(), beanName,
dummyBean.strategy());
dummyBean.contextName(), dummyBean.strategy());
}
// Bare bone, "dummy", implementation that should not override anything
@@ -62,9 +64,9 @@ import org.springframework.util.StringUtils;
static class DummyBeanOverrideHandler extends BeanOverrideHandler {
DummyBeanOverrideHandler(Field field, Class<?> typeToOverride, @Nullable String beanName,
BeanOverrideStrategy strategy) {
String contextName, BeanOverrideStrategy strategy) {
super(field, ResolvableType.forClass(typeToOverride), beanName, strategy);
super(field, ResolvableType.forClass(typeToOverride), beanName, contextName, strategy);
}
@Override

View File

@@ -130,7 +130,7 @@ class TestBeanOverrideHandlerTests {
TestBean annotation = field.getAnnotation(TestBean.class);
String beanName = (StringUtils.hasText(annotation.name()) ? annotation.name() : null);
return new TestBeanOverrideHandler(
field, ResolvableType.forClass(field.getType()), beanName, BeanOverrideStrategy.REPLACE, overrideMethod);
field, ResolvableType.forClass(field.getType()), beanName, "", BeanOverrideStrategy.REPLACE, overrideMethod);
}
static class SampleOneOverride {

View File

@@ -0,0 +1,109 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.convention.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.convention.TestBean;
import org.springframework.test.context.bean.override.convention.hierarchies.TestBeanByNameInChildContextHierarchyTests.Config1;
import org.springframework.test.context.bean.override.convention.hierarchies.TestBeanByNameInChildContextHierarchyTests.Config2;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Verifies that {@link TestBean @TestBean} can be used within a
* {@link ContextHierarchy @ContextHierarchy} with named context levels, when
* a bean is only overridden "by name" in the child.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class),
@ContextConfiguration(classes = Config2.class, name = "child")
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class TestBeanByNameInChildContextHierarchyTests {
@TestBean(name = "service", contextName = "child")
ExampleService service;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
static ExampleService service() {
return () -> "@TestBean 2";
}
@Test
void test(ApplicationContext context) {
ExampleService serviceInParent = context.getParent().getBean(ExampleService.class);
assertThat(service.greeting()).isEqualTo("@TestBean 2");
assertThat(serviceCaller1.getService()).isSameAs(serviceInParent);
assertThat(serviceCaller2.getService()).isSameAs(service);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say Service 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say @TestBean 2");
}
@Configuration
static class Config1 {
@Bean
ExampleService service() {
return () -> "Service 1";
}
@Bean
ExampleServiceCaller serviceCaller1(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
@Configuration
static class Config2 {
@Bean
ExampleService service() {
return () -> "Service 2";
}
@Bean
ExampleServiceCaller serviceCaller2(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.convention.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.convention.TestBean;
import org.springframework.test.context.bean.override.convention.hierarchies.TestBeanByNameInParentAndChildContextHierarchyTests.Config1;
import org.springframework.test.context.bean.override.convention.hierarchies.TestBeanByNameInParentAndChildContextHierarchyTests.Config2;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Verifies that {@link TestBean @TestBean} can be used within a
* {@link ContextHierarchy @ContextHierarchy} with named context levels, when
* identical beans are overridden "by name" in the parent and in the child.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class, name = "parent"),
@ContextConfiguration(classes = Config2.class, name = "child")
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class TestBeanByNameInParentAndChildContextHierarchyTests {
@TestBean(name = "service", contextName = "parent")
ExampleService serviceInParent;
@TestBean(name = "service", contextName = "child")
ExampleService serviceInChild;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
static ExampleService serviceInParent() {
return () -> "@TestBean 1";
}
static ExampleService serviceInChild() {
return () -> "@TestBean 2";
}
@Test
void test() {
assertThat(serviceInParent.greeting()).isEqualTo("@TestBean 1");
assertThat(serviceInChild.greeting()).isEqualTo("@TestBean 2");
assertThat(serviceCaller1.getService()).isSameAs(serviceInParent);
assertThat(serviceCaller2.getService()).isSameAs(serviceInChild);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say @TestBean 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say @TestBean 2");
}
@Configuration
static class Config1 {
@Bean
ExampleService service() {
return () -> "Service 1";
}
@Bean
ExampleServiceCaller serviceCaller1(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
@Configuration
static class Config2 {
@Bean
ExampleService service() {
return () -> "Service 2";
}
@Bean
ExampleServiceCaller serviceCaller2(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.convention.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.convention.TestBean;
import org.springframework.test.context.bean.override.convention.hierarchies.TestBeanByNameInParentContextHierarchyTests.Config1;
import org.springframework.test.context.bean.override.convention.hierarchies.TestBeanByNameInParentContextHierarchyTests.Config2;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Verifies that {@link TestBean @TestBean} can be used within a
* {@link ContextHierarchy @ContextHierarchy} with named context levels, when
* a bean is only overridden "by name" in the parent.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class, name = "parent"),
@ContextConfiguration(classes = Config2.class)
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class TestBeanByNameInParentContextHierarchyTests {
@TestBean(name = "service", contextName = "parent")
ExampleService service;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
static ExampleService service() {
return () -> "@TestBean 1";
}
@Test
void test() {
assertThat(service.greeting()).isEqualTo("@TestBean 1");
assertThat(serviceCaller1.getService()).isSameAs(service);
assertThat(serviceCaller2.getService()).isSameAs(service);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say @TestBean 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say @TestBean 1");
}
@Configuration
static class Config1 {
@Bean
ExampleService service() {
return () -> "Service 1";
}
@Bean
ExampleServiceCaller serviceCaller1(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
@Configuration
static class Config2 {
@Bean
ExampleServiceCaller serviceCaller2(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.convention.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.convention.TestBean;
import org.springframework.test.context.bean.override.convention.hierarchies.TestBeanByTypeInChildContextHierarchyTests.Config1;
import org.springframework.test.context.bean.override.convention.hierarchies.TestBeanByTypeInChildContextHierarchyTests.Config2;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Verifies that {@link TestBean @TestBean} can be used within a
* {@link ContextHierarchy @ContextHierarchy} with named context levels, when
* a bean is only overridden "by type" in the child.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class),
@ContextConfiguration(classes = Config2.class, name = "child")
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class TestBeanByTypeInChildContextHierarchyTests {
@TestBean(contextName = "child")
ExampleService service;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
static ExampleService service() {
return () -> "@TestBean 2";
}
@Test
void test(ApplicationContext context) {
ExampleService serviceInParent = context.getParent().getBean(ExampleService.class);
assertThat(service.greeting()).isEqualTo("@TestBean 2");
assertThat(serviceCaller1.getService()).isSameAs(serviceInParent);
assertThat(serviceCaller2.getService()).isSameAs(service);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say Service 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say @TestBean 2");
}
@Configuration
static class Config1 {
@Bean
ExampleService service() {
return () -> "Service 1";
}
@Bean
ExampleServiceCaller serviceCaller1(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
@Configuration
static class Config2 {
@Bean
ExampleService service() {
return () -> "Service 2";
}
@Bean
ExampleServiceCaller serviceCaller2(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.convention.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.convention.TestBean;
import org.springframework.test.context.bean.override.convention.hierarchies.TestBeanByTypeInParentAndChildContextHierarchyTests.Config1;
import org.springframework.test.context.bean.override.convention.hierarchies.TestBeanByTypeInParentAndChildContextHierarchyTests.Config2;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Verifies that {@link TestBean @TestBean} can be used within a
* {@link ContextHierarchy @ContextHierarchy} with named context levels, when
* identical beans are overridden "by type" in the parent and in the child.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class, name = "parent"),
@ContextConfiguration(classes = Config2.class, name = "child")
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class TestBeanByTypeInParentAndChildContextHierarchyTests {
@TestBean(contextName = "parent")
ExampleService serviceInParent;
@TestBean(contextName = "child")
ExampleService serviceInChild;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
static ExampleService serviceInParent() {
return () -> "@TestBean 1";
}
static ExampleService serviceInChild() {
return () -> "@TestBean 2";
}
@Test
void test() {
assertThat(serviceInParent.greeting()).isEqualTo("@TestBean 1");
assertThat(serviceInChild.greeting()).isEqualTo("@TestBean 2");
assertThat(serviceCaller1.getService()).isSameAs(serviceInParent);
assertThat(serviceCaller2.getService()).isSameAs(serviceInChild);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say @TestBean 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say @TestBean 2");
}
@Configuration
static class Config1 {
@Bean
ExampleService service() {
return () -> "Service 1";
}
@Bean
ExampleServiceCaller serviceCaller1(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
@Configuration
static class Config2 {
@Bean
ExampleService service() {
return () -> "Service 2";
}
@Bean
ExampleServiceCaller serviceCaller2(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.convention.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.convention.TestBean;
import org.springframework.test.context.bean.override.convention.hierarchies.TestBeanByTypeInParentContextHierarchyTests.Config1;
import org.springframework.test.context.bean.override.convention.hierarchies.TestBeanByTypeInParentContextHierarchyTests.Config2;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Verifies that {@link TestBean @TestBean} can be used within a
* {@link ContextHierarchy @ContextHierarchy} with named context levels, when
* a bean is only overridden "by type" in the parent.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class, name = "parent"),
@ContextConfiguration(classes = Config2.class)
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class TestBeanByTypeInParentContextHierarchyTests {
@TestBean(contextName = "parent")
ExampleService service;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
static ExampleService service() {
return () -> "@TestBean 1";
}
@Test
void test() {
assertThat(service.greeting()).isEqualTo("@TestBean 1");
assertThat(serviceCaller1.getService()).isSameAs(service);
assertThat(serviceCaller2.getService()).isSameAs(service);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say @TestBean 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say @TestBean 1");
}
@Configuration
static class Config1 {
@Bean
ExampleService service() {
return () -> "Service 1";
}
@Bean
ExampleServiceCaller serviceCaller1(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
@Configuration
static class Config2 {
@Bean
ExampleServiceCaller serviceCaller2(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,7 +43,7 @@ class EasyMockBeanOverrideHandler extends BeanOverrideHandler {
EasyMockBeanOverrideHandler(Field field, Class<?> typeToOverride, @Nullable String beanName,
MockType mockType) {
super(field, ResolvableType.forClass(typeToOverride), beanName, REPLACE_OR_CREATE);
super(field, ResolvableType.forClass(typeToOverride), beanName, "", REPLACE_OR_CREATE);
this.mockType = mockType;
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.hierarchies;
class BarService {
String bar() {
return "bar";
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.hierarchies;
import jakarta.annotation.PostConstruct;
import org.springframework.context.annotation.Configuration;
@Configuration
class ErrorIfContextReloadedConfig {
private static boolean loaded = false;
@PostConstruct
public void postConstruct() {
if (loaded) {
throw new RuntimeException("Context loaded multiple times");
}
loaded = true;
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.hierarchies;
class FooService {
String foo() {
return "foo";
}
}

View File

@@ -14,12 +14,11 @@
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.integration;
package org.springframework.test.context.bean.override.mockito.hierarchies;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.bean.override.example.ExampleService;
@@ -45,8 +44,6 @@ public class MockitoBeanAndContextHierarchyParentIntegrationTests {
@MockitoBean
ExampleService service;
@Autowired
ApplicationContext context;
@BeforeEach
void configureServiceMock() {
@@ -54,7 +51,7 @@ public class MockitoBeanAndContextHierarchyParentIntegrationTests {
}
@Test
void test() {
void test(ApplicationContext context) {
assertThat(context.getBeanNamesForType(ExampleService.class)).hasSize(1);
assertThat(context.getBeanNamesForType(ExampleServiceCaller.class)).isEmpty();

View File

@@ -0,0 +1,111 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.bean.override.example.RealExampleService;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoBeanByNameInChildContextHierarchyTests.Config1;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoBeanByNameInChildContextHierarchyTests.Config2;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
import static org.springframework.test.mockito.MockitoAssertions.assertIsNotMock;
/**
* Verifies that {@link MockitoBean @MockitoBean} can be used within a
* {@link ContextHierarchy @ContextHierarchy} with named context levels, when
* a bean is only mocked "by name" in the child.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class),
@ContextConfiguration(classes = Config2.class, name = "child")
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class MockitoBeanByNameInChildContextHierarchyTests {
@MockitoBean(name = "service", contextName = "child")
ExampleService service;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
@Test
void test(ApplicationContext context) {
ExampleService serviceInParent = context.getParent().getBean(ExampleService.class);
assertIsNotMock(serviceInParent);
when(service.greeting()).thenReturn("Mock 2");
assertThat(service.greeting()).isEqualTo("Mock 2");
assertThat(serviceCaller1.getService()).isSameAs(serviceInParent);
assertThat(serviceCaller2.getService()).isSameAs(service);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say Service 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say Mock 2");
}
@Configuration
static class Config1 {
@Bean
ExampleService service() {
return new RealExampleService("Service 1");
}
@Bean
ExampleServiceCaller serviceCaller1(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
@Configuration
static class Config2 {
@Bean
ExampleService service() {
return new RealExampleService("Service 2");
}
@Bean
ExampleServiceCaller serviceCaller2(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.bean.override.example.RealExampleService;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoBeanByNameInParentAndChildContextHierarchyTests.Config1;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoBeanByNameInParentAndChildContextHierarchyTests.Config2;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
/**
* Verifies that {@link MockitoBean @MockitoBean} can be used within a
* {@link ContextHierarchy @ContextHierarchy} with named context levels, when
* identical beans are mocked "by name" in the parent and in the child.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class, name = "parent"),
@ContextConfiguration(classes = Config2.class, name = "child")
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class MockitoBeanByNameInParentAndChildContextHierarchyTests {
@MockitoBean(name = "service", contextName = "parent")
ExampleService serviceInParent;
@MockitoBean(name = "service", contextName = "child")
ExampleService serviceInChild;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
@Test
void test() {
when(serviceInParent.greeting()).thenReturn("Mock 1");
when(serviceInChild.greeting()).thenReturn("Mock 2");
assertThat(serviceInParent.greeting()).isEqualTo("Mock 1");
assertThat(serviceInChild.greeting()).isEqualTo("Mock 2");
assertThat(serviceCaller1.getService()).isSameAs(serviceInParent);
assertThat(serviceCaller2.getService()).isSameAs(serviceInChild);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say Mock 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say Mock 2");
}
@Configuration
static class Config1 {
@Bean
ExampleService service() {
return new RealExampleService("Service 1");
}
@Bean
ExampleServiceCaller serviceCaller1(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
@Configuration
static class Config2 {
@Bean
ExampleService service() {
return new RealExampleService("Service 2");
}
@Bean
ExampleServiceCaller serviceCaller2(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.bean.override.example.RealExampleService;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoBeanByNameInParentContextHierarchyTests.Config1;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoBeanByNameInParentContextHierarchyTests.Config2;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
/**
* Verifies that {@link MockitoBean @MockitoBean} can be used within a
* {@link ContextHierarchy @ContextHierarchy} with named context levels, when
* a bean is only mocked "by name" in the parent.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class, name = "parent"),
@ContextConfiguration(classes = Config2.class)
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class MockitoBeanByNameInParentContextHierarchyTests {
@MockitoBean(name = "service", contextName = "parent")
ExampleService service;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
@Test
void test() {
when(service.greeting()).thenReturn("Mock 1");
assertThat(service.greeting()).isEqualTo("Mock 1");
assertThat(serviceCaller1.getService()).isSameAs(service);
assertThat(serviceCaller2.getService()).isSameAs(service);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say Mock 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say Mock 1");
}
@Configuration
static class Config1 {
@Bean
ExampleService service() {
return new RealExampleService("Service 1");
}
@Bean
ExampleServiceCaller serviceCaller1(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
@Configuration
static class Config2 {
@Bean
ExampleServiceCaller serviceCaller2(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.bean.override.example.RealExampleService;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoBeanByTypeInChildContextHierarchyTests.Config1;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoBeanByTypeInChildContextHierarchyTests.Config2;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
import static org.springframework.test.mockito.MockitoAssertions.assertIsNotMock;
/**
* Verifies that {@link MockitoBean @MockitoBean} can be used within a
* {@link ContextHierarchy @ContextHierarchy} with named context levels, when
* a bean is only mocked "by type" in the child.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class),
@ContextConfiguration(classes = Config2.class, name = "child")
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class MockitoBeanByTypeInChildContextHierarchyTests {
@MockitoBean(contextName = "child")
ExampleService service;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
@Test
void test(ApplicationContext context) {
ExampleService serviceInParent = context.getParent().getBean(ExampleService.class);
assertIsNotMock(serviceInParent);
when(service.greeting()).thenReturn("Mock 2");
assertThat(service.greeting()).isEqualTo("Mock 2");
assertThat(serviceCaller1.getService()).isSameAs(serviceInParent);
assertThat(serviceCaller2.getService()).isSameAs(service);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say Service 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say Mock 2");
}
@Configuration
static class Config1 {
@Bean
ExampleService service() {
return new RealExampleService("Service 1");
}
@Bean
ExampleServiceCaller serviceCaller1(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
@Configuration
static class Config2 {
@Bean
ExampleService service() {
return new RealExampleService("Service 2");
}
@Bean
ExampleServiceCaller serviceCaller2(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.bean.override.example.RealExampleService;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoBeanByTypeInParentAndChildContextHierarchyTests.Config1;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoBeanByTypeInParentAndChildContextHierarchyTests.Config2;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
/**
* Verifies that {@link MockitoBean @MockitoBean} can be used within a
* {@link ContextHierarchy @ContextHierarchy} with named context levels, when
* identical beans are mocked "by type" in the parent and in the child.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class, name = "parent"),
@ContextConfiguration(classes = Config2.class, name = "child")
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class MockitoBeanByTypeInParentAndChildContextHierarchyTests {
@MockitoBean(contextName = "parent")
ExampleService serviceInParent;
@MockitoBean(contextName = "child")
ExampleService serviceInChild;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
@Test
void test() {
when(serviceInParent.greeting()).thenReturn("Mock 1");
when(serviceInChild.greeting()).thenReturn("Mock 2");
assertThat(serviceInParent.greeting()).isEqualTo("Mock 1");
assertThat(serviceInChild.greeting()).isEqualTo("Mock 2");
assertThat(serviceCaller1.getService()).isSameAs(serviceInParent);
assertThat(serviceCaller2.getService()).isSameAs(serviceInChild);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say Mock 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say Mock 2");
}
@Configuration
static class Config1 {
@Bean
ExampleService service() {
return new RealExampleService("Service 1");
}
@Bean
ExampleServiceCaller serviceCaller1(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
@Configuration
static class Config2 {
@Bean
ExampleService service() {
return new RealExampleService("Service 2");
}
@Bean
ExampleServiceCaller serviceCaller2(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.bean.override.example.RealExampleService;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoBeanByTypeInParentContextHierarchyTests.Config1;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoBeanByTypeInParentContextHierarchyTests.Config2;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
/**
* Verifies that {@link MockitoBean @MockitoBean} can be used within a
* {@link ContextHierarchy @ContextHierarchy} with named context levels, when
* a bean is only mocked "by type" in the parent.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class, name = "parent"),
@ContextConfiguration(classes = Config2.class)
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class MockitoBeanByTypeInParentContextHierarchyTests {
@MockitoBean(contextName = "parent")
ExampleService service;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
@Test
void test() {
when(service.greeting()).thenReturn("Mock 1");
assertThat(service.greeting()).isEqualTo("Mock 1");
assertThat(serviceCaller1.getService()).isSameAs(service);
assertThat(serviceCaller2.getService()).isSameAs(service);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say Mock 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say Mock 1");
}
@Configuration
static class Config1 {
@Bean
ExampleService service() {
return new RealExampleService("Service 1");
}
@Bean
ExampleServiceCaller serviceCaller1(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
@Configuration
static class Config2 {
@Bean
ExampleServiceCaller serviceCaller2(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,10 @@
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.integration;
package org.springframework.test.context.bean.override.mockito.hierarchies;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -50,18 +49,14 @@ public class MockitoSpyBeanAndContextHierarchyChildIntegrationTests extends
@MockitoSpyBean
ExampleServiceCaller serviceCaller;
@Autowired
ApplicationContext context;
@Test
@Override
void test() {
assertThat(context).as("child ApplicationContext").isNotNull();
assertThat(context.getParent()).as("parent ApplicationContext").isNotNull();
assertThat(context.getParent().getParent()).as("grandparent ApplicationContext").isNull();
void test(ApplicationContext context) {
ApplicationContext parentContext = context.getParent();
assertThat(parentContext).as("parent ApplicationContext").isNotNull();
assertThat(parentContext.getParent()).as("grandparent ApplicationContext").isNull();
assertThat(parentContext.getBeanNamesForType(ExampleService.class)).hasSize(1);
assertThat(parentContext.getBeanNamesForType(ExampleServiceCaller.class)).isEmpty();

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.bean.override.example.RealExampleService;
import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoSpyBeanByNameInChildContextHierarchyTests.Config1;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoSpyBeanByNameInChildContextHierarchyTests.Config2;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.mockito.MockitoAssertions.assertIsNotSpy;
import static org.springframework.test.mockito.MockitoAssertions.assertIsSpy;
/**
* Verifies that {@link MockitoSpyBean @MockitoSpyBean} can be used within a
* {@link ContextHierarchy @ContextHierarchy} with named context levels, when
* a bean is only spied on "by name" in the child.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class),
@ContextConfiguration(classes = Config2.class, name = "child")
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class MockitoSpyBeanByNameInChildContextHierarchyTests {
@MockitoSpyBean(name = "service", contextName = "child")
ExampleService service;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
@Test
void test(ApplicationContext context) {
ExampleService serviceInParent = context.getParent().getBean(ExampleService.class);
assertIsNotSpy(serviceInParent);
assertIsSpy(service);
assertThat(service.greeting()).isEqualTo("Service 2");
assertThat(serviceCaller1.getService()).isSameAs(serviceInParent);
assertThat(serviceCaller2.getService()).isSameAs(service);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say Service 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say Service 2");
}
@Configuration
static class Config1 {
@Bean
ExampleService service() {
return new RealExampleService("Service 1");
}
@Bean
ExampleServiceCaller serviceCaller1(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
@Configuration
static class Config2 {
@Bean
ExampleService service() {
return new RealExampleService("Service 2");
}
@Bean
ExampleServiceCaller serviceCaller2(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.bean.override.example.RealExampleService;
import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoSpyBeanByNameInParentAndChildContextHierarchyTests.Config1;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoSpyBeanByNameInParentAndChildContextHierarchyTests.Config2;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.mockito.MockitoAssertions.assertIsSpy;
/**
* Verifies that {@link MockitoSpyBean @MockitoSpyBean} can be used within a
* {@link ContextHierarchy @ContextHierarchy} with named context levels, when
* identical beans are spied on "by name" in the parent and in the child.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class, name = "parent"),
@ContextConfiguration(classes = Config2.class, name = "child")
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class MockitoSpyBeanByNameInParentAndChildContextHierarchyTests {
@MockitoSpyBean(name = "service", contextName = "parent")
ExampleService serviceInParent;
@MockitoSpyBean(name = "service", contextName = "child")
ExampleService serviceInChild;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
@Test
void test() {
assertIsSpy(serviceInParent);
assertIsSpy(serviceInChild);
assertThat(serviceInParent.greeting()).isEqualTo("Service 1");
assertThat(serviceInChild.greeting()).isEqualTo("Service 2");
assertThat(serviceCaller1.getService()).isSameAs(serviceInParent);
assertThat(serviceCaller2.getService()).isSameAs(serviceInChild);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say Service 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say Service 2");
}
@Configuration
static class Config1 {
@Bean
ExampleService service() {
return new RealExampleService("Service 1");
}
@Bean
ExampleServiceCaller serviceCaller1(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
@Configuration
static class Config2 {
@Bean
ExampleService service() {
return new RealExampleService("Service 2");
}
@Bean
ExampleServiceCaller serviceCaller2(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.mockito.MockitoAssertions.assertIsSpy;
/**
* This is effectively a one-to-one copy of
* {@link MockitoSpyBeanByNameInParentAndChildContextHierarchyTests}, except
* that this test class uses different names for the context hierarchy levels:
* level-1 and level-2 instead of parent and child.
*
* <p>If the context cache is broken, either this test class or
* {@code MockitoSpyBeanByNameInParentAndChildContextHierarchyTests} will fail
* when run within the same test suite.
*
* @author Sam Brannen
* @since 6.2.6
* @see MockitoSpyBeanByNameInParentAndChildContextHierarchyTests
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = MockitoSpyBeanByNameInParentAndChildContextHierarchyTests.Config1.class, name = "level-1"),
@ContextConfiguration(classes = MockitoSpyBeanByNameInParentAndChildContextHierarchyTests.Config2.class, name = "level-2")
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class MockitoSpyBeanByNameInParentAndChildContextHierarchyV2Tests {
@MockitoSpyBean(name = "service", contextName = "level-1")
ExampleService serviceInParent;
@MockitoSpyBean(name = "service", contextName = "level-2")
ExampleService serviceInChild;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
@Test
void test() {
assertIsSpy(serviceInParent);
assertIsSpy(serviceInChild);
assertThat(serviceInParent.greeting()).isEqualTo("Service 1");
assertThat(serviceInChild.greeting()).isEqualTo("Service 2");
assertThat(serviceCaller1.getService()).isSameAs(serviceInParent);
assertThat(serviceCaller2.getService()).isSameAs(serviceInChild);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say Service 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say Service 2");
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.bean.override.example.RealExampleService;
import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoSpyBeanByNameInParentContextHierarchyTests.Config1;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoSpyBeanByNameInParentContextHierarchyTests.Config2;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.mockito.MockitoAssertions.assertIsSpy;
/**
* Verifies that {@link MockitoSpyBean @MockitoSpyBean} can be used within a
* {@link ContextHierarchy @ContextHierarchy} with named context levels, when
* a bean is only spied on "by name" in the parent.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class, name = "parent"),
@ContextConfiguration(classes = Config2.class)
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class MockitoSpyBeanByNameInParentContextHierarchyTests {
@MockitoSpyBean(name = "service", contextName = "parent")
ExampleService service;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
@Test
void test() {
assertIsSpy(service);
assertThat(service.greeting()).isEqualTo("Service 1");
assertThat(serviceCaller1.getService()).isSameAs(service);
assertThat(serviceCaller2.getService()).isSameAs(service);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say Service 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say Service 1");
}
@Configuration
static class Config1 {
@Bean
ExampleService service() {
return new RealExampleService("Service 1");
}
@Bean
ExampleServiceCaller serviceCaller1(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
@Configuration
static class Config2 {
@Bean
ExampleServiceCaller serviceCaller2(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.bean.override.example.RealExampleService;
import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoSpyBeanByTypeInChildContextHierarchyTests.Config1;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoSpyBeanByTypeInChildContextHierarchyTests.Config2;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.mockito.MockitoAssertions.assertIsNotSpy;
import static org.springframework.test.mockito.MockitoAssertions.assertIsSpy;
/**
* Verifies that {@link MockitoSpyBean @MockitoSpyBean} can be used within a
* {@link ContextHierarchy @ContextHierarchy} with named context levels, when
* a bean is only spied on "by type" in the child.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class),
@ContextConfiguration(classes = Config2.class, name = "child")
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class MockitoSpyBeanByTypeInChildContextHierarchyTests {
@MockitoSpyBean(contextName = "child")
ExampleService service;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
@Test
void test(ApplicationContext context) {
ExampleService serviceInParent = context.getParent().getBean(ExampleService.class);
assertIsNotSpy(serviceInParent);
assertIsSpy(service);
assertThat(service.greeting()).isEqualTo("Service 2");
assertThat(serviceCaller1.getService()).isSameAs(serviceInParent);
assertThat(serviceCaller2.getService()).isSameAs(service);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say Service 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say Service 2");
}
@Configuration
static class Config1 {
@Bean
ExampleService service() {
return new RealExampleService("Service 1");
}
@Bean
ExampleServiceCaller serviceCaller1(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
@Configuration
static class Config2 {
@Bean
ExampleService service() {
return new RealExampleService("Service 2");
}
@Bean
ExampleServiceCaller serviceCaller2(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.bean.override.example.RealExampleService;
import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoSpyBeanByTypeInParentAndChildContextHierarchyTests.Config1;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoSpyBeanByTypeInParentAndChildContextHierarchyTests.Config2;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.mockito.MockitoAssertions.assertIsSpy;
/**
* Verifies that {@link MockitoSpyBean @MockitoSpyBean} can be used within a
* {@link ContextHierarchy @ContextHierarchy} with named context levels, when
* identical beans are spied on "by type" in the parent and in the child.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class, name = "parent"),
@ContextConfiguration(classes = Config2.class, name = "child")
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class MockitoSpyBeanByTypeInParentAndChildContextHierarchyTests {
@MockitoSpyBean(contextName = "parent")
ExampleService serviceInParent;
@MockitoSpyBean(contextName = "child")
ExampleService serviceInChild;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
@Test
void test() {
assertIsSpy(serviceInParent);
assertIsSpy(serviceInChild);
assertThat(serviceInParent.greeting()).isEqualTo("Service 1");
assertThat(serviceInChild.greeting()).isEqualTo("Service 2");
assertThat(serviceCaller1.getService()).isSameAs(serviceInParent);
assertThat(serviceCaller2.getService()).isSameAs(serviceInChild);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say Service 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say Service 2");
}
@Configuration
static class Config1 {
@Bean
ExampleService service() {
return new RealExampleService("Service 1");
}
@Bean
ExampleServiceCaller serviceCaller1(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
@Configuration
static class Config2 {
@Bean
ExampleService service() {
return new RealExampleService("Service 2");
}
@Bean
ExampleServiceCaller serviceCaller2(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.bean.override.example.RealExampleService;
import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoSpyBeanByTypeInParentContextHierarchyTests.Config1;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoSpyBeanByTypeInParentContextHierarchyTests.Config2;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.mockito.MockitoAssertions.assertIsSpy;
/**
* Verifies that {@link MockitoSpyBean @MockitoSpyBean} can be used within a
* {@link ContextHierarchy @ContextHierarchy} with named context levels, when
* a bean is only spied on "by type" in the parent.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class, name = "parent"),
@ContextConfiguration(classes = Config2.class)
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class MockitoSpyBeanByTypeInParentContextHierarchyTests {
@MockitoSpyBean(contextName = "parent")
ExampleService service;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
@Test
void test() {
assertIsSpy(service);
assertThat(service.greeting()).isEqualTo("Service 1");
assertThat(serviceCaller1.getService()).isSameAs(service);
assertThat(serviceCaller2.getService()).isSameAs(service);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say Service 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say Service 1");
}
@Configuration
static class Config1 {
@Bean
ExampleService service() {
return new RealExampleService("Service 1");
}
@Bean
ExampleServiceCaller serviceCaller1(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
@Configuration
static class Config2 {
@Bean
ExampleServiceCaller serviceCaller2(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.example.ExampleService;
import org.springframework.test.context.bean.override.example.ExampleServiceCaller;
import org.springframework.test.context.bean.override.example.RealExampleService;
import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoSpyBeansByTypeInParentAndChildContextHierarchyTests.Config1;
import org.springframework.test.context.bean.override.mockito.hierarchies.MockitoSpyBeansByTypeInParentAndChildContextHierarchyTests.Config2;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.mockito.MockitoAssertions.assertIsSpy;
/**
* Verifies that {@link MockitoSpyBean @MockitoSpyBean} can be used within a
* {@link ContextHierarchy @ContextHierarchy} with named context levels, when
* identical beans are spied on "by type" in the parent and in the child and
* configured via class-level {@code @MockitoSpyBean} declarations.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = Config1.class, name = "parent"),
@ContextConfiguration(classes = Config2.class, name = "child")
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
@MockitoSpyBean(types = ExampleService.class, contextName = "parent")
@MockitoSpyBean(types = ExampleService.class, contextName = "child")
class MockitoSpyBeansByTypeInParentAndChildContextHierarchyTests {
@Autowired
ExampleService serviceInChild;
@Autowired
ExampleServiceCaller serviceCaller1;
@Autowired
ExampleServiceCaller serviceCaller2;
@Test
void test(ApplicationContext context) {
ExampleService serviceInParent = context.getParent().getBean(ExampleService.class);
assertIsSpy(serviceInParent);
assertIsSpy(serviceInChild);
assertThat(serviceInParent.greeting()).isEqualTo("Service 1");
assertThat(serviceInChild.greeting()).isEqualTo("Service 2");
assertThat(serviceCaller1.getService()).isSameAs(serviceInParent);
assertThat(serviceCaller2.getService()).isSameAs(serviceInChild);
assertThat(serviceCaller1.sayGreeting()).isEqualTo("I say Service 1");
assertThat(serviceCaller2.sayGreeting()).isEqualTo("I say Service 2");
}
@Configuration
static class Config1 {
@Bean
ExampleService service() {
return new RealExampleService("Service 1");
}
@Bean
ExampleServiceCaller serviceCaller1(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
@Configuration
static class Config2 {
@Bean
ExampleService service() {
return new RealExampleService("Service 2");
}
@Bean
ExampleServiceCaller serviceCaller2(ExampleService service) {
return new ExampleServiceCaller(service);
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
/**
* If the {@link ApplicationContext} for {@link ErrorIfContextReloadedConfig} is
* loaded twice (i.e., not properly cached), either this test class or
* {@link ReusedParentConfigV2Tests} will fail when both test classes are run
* within the same test suite.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = ErrorIfContextReloadedConfig.class),
@ContextConfiguration(classes = FooService.class, name = "child")
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class ReusedParentConfigV1Tests {
@Autowired
ErrorIfContextReloadedConfig sharedConfig;
@MockitoBean(contextName = "child")
FooService fooService;
@Test
void test(ApplicationContext context) {
assertThat(context.getParent().getBeanNamesForType(FooService.class)).isEmpty();
assertThat(context.getBeanNamesForType(FooService.class)).hasSize(1);
given(fooService.foo()).willReturn("mock");
assertThat(fooService.foo()).isEqualTo("mock");
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2002-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito.hierarchies;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextHierarchy;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
/**
* If the {@link ApplicationContext} for {@link ErrorIfContextReloadedConfig} is
* loaded twice (i.e., not properly cached), either this test class or
* {@link ReusedParentConfigV1Tests} will fail when both test classes are run
* within the same test suite.
*
* @author Sam Brannen
* @since 6.2.6
*/
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(classes = ErrorIfContextReloadedConfig.class),
@ContextConfiguration(classes = BarService.class, name = "child")
})
@DisabledInAotMode("@ContextHierarchy is not supported in AOT")
class ReusedParentConfigV2Tests {
@Autowired
ErrorIfContextReloadedConfig sharedConfig;
@MockitoBean(contextName = "child")
BarService barService;
@Test
void test(ApplicationContext context) {
assertThat(context.getParent().getBeanNamesForType(BarService.class)).isEmpty();
assertThat(context.getBeanNamesForType(BarService.class)).hasSize(1);
given(barService.bar()).willReturn("mock");
assertThat(barService.bar()).isEqualTo("mock");
}
}