Skip to content

Commit 68fcf81

Browse files
committed
Merge branch '6.2.x'
2 parents 3fff3b8 + 305686d commit 68fcf81

File tree

3 files changed

+86
-6
lines changed

3 files changed

+86
-6
lines changed

spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideHandler.java

+9-5
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ static List<BeanOverrideHandler> findAllHandlers(Class<?> testClass) {
131131

132132
private static List<BeanOverrideHandler> findHandlers(Class<?> testClass, boolean localFieldsOnly) {
133133
List<BeanOverrideHandler> handlers = new ArrayList<>();
134-
findHandlers(testClass, testClass, handlers, localFieldsOnly);
134+
findHandlers(testClass, testClass, handlers, localFieldsOnly, new HashSet<>());
135135
return handlers;
136136
}
137137

@@ -145,26 +145,30 @@ private static List<BeanOverrideHandler> findHandlers(Class<?> testClass, boolea
145145
* @param testClass the original test class
146146
* @param handlers the list of handlers found
147147
* @param localFieldsOnly whether to search only on local fields within the type hierarchy
148+
* @param visitedEnclosingClasses the set of enclosing classes already visited
148149
* @since 6.2.2
149150
*/
150151
private static void findHandlers(Class<?> clazz, Class<?> testClass, List<BeanOverrideHandler> handlers,
151-
boolean localFieldsOnly) {
152+
boolean localFieldsOnly, Set<Class<?>> visitedEnclosingClasses) {
152153

153154
// 1) Search enclosing class hierarchy.
154155
if (!localFieldsOnly && TestContextAnnotationUtils.searchEnclosingClass(clazz)) {
155-
findHandlers(clazz.getEnclosingClass(), testClass, handlers, localFieldsOnly);
156+
Class<?> enclosingClass = clazz.getEnclosingClass();
157+
if (visitedEnclosingClasses.add(enclosingClass)) {
158+
findHandlers(enclosingClass, testClass, handlers, localFieldsOnly, visitedEnclosingClasses);
159+
}
156160
}
157161

158162
// 2) Search class hierarchy.
159163
Class<?> superclass = clazz.getSuperclass();
160164
if (superclass != null && superclass != Object.class) {
161-
findHandlers(superclass, testClass, handlers, localFieldsOnly);
165+
findHandlers(superclass, testClass, handlers, localFieldsOnly, visitedEnclosingClasses);
162166
}
163167

164168
if (!localFieldsOnly) {
165169
// 3) Search interfaces.
166170
for (Class<?> ifc : clazz.getInterfaces()) {
167-
findHandlers(ifc, testClass, handlers, localFieldsOnly);
171+
findHandlers(ifc, testClass, handlers, localFieldsOnly, visitedEnclosingClasses);
168172
}
169173

170174
// 4) Process current class.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright 2002-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.test.context.bean.override.mockito;
18+
19+
import org.junit.jupiter.api.Nested;
20+
import org.junit.jupiter.api.Test;
21+
import org.junit.jupiter.api.extension.ExtendWith;
22+
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
import org.springframework.context.ApplicationContext;
25+
import org.springframework.test.context.bean.override.example.ExampleService;
26+
import org.springframework.test.context.junit.jupiter.SpringExtension;
27+
28+
import static org.assertj.core.api.Assertions.assertThat;
29+
import static org.springframework.test.mockito.MockitoAssertions.assertIsMock;
30+
31+
/**
32+
* Integration tests for {@link MockitoBean @MockitoBean} which verify that
33+
* {@code @MockitoBean} fields are not discovered more than once when searching
34+
* intertwined enclosing class hierarchies and type hierarchies.
35+
*
36+
* @author Sam Brannen
37+
* @since 6.2.3
38+
* @see <a href="https://github.com/spring-projects/spring-framework/issues/34324">gh-34324</a>
39+
*/
40+
@ExtendWith(SpringExtension.class)
41+
class MockitoBeanNestedAndTypeHierarchiesTests {
42+
43+
@Autowired
44+
ApplicationContext enclosingContext;
45+
46+
@MockitoBean
47+
ExampleService service;
48+
49+
50+
@Test
51+
void topLevelTest() {
52+
assertIsMock(service);
53+
54+
// The following are prerequisites for the reported regression.
55+
assertThat(NestedTests.class.getSuperclass())
56+
.isEqualTo(AbstractBaseClassForNestedTests.class);
57+
assertThat(NestedTests.class.getEnclosingClass())
58+
.isEqualTo(AbstractBaseClassForNestedTests.class.getEnclosingClass())
59+
.isEqualTo(getClass());
60+
}
61+
62+
63+
abstract class AbstractBaseClassForNestedTests {
64+
65+
@Test
66+
void nestedTest(ApplicationContext nestedContext) {
67+
assertIsMock(service);
68+
assertThat(enclosingContext).isSameAs(nestedContext);
69+
}
70+
}
71+
72+
@Nested
73+
class NestedTests extends AbstractBaseClassForNestedTests {
74+
}
75+
76+
}

spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/mockbeans/MockitoBeansByTypeIntegrationTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ void checkMocks() {
9090

9191

9292
@MockitoBean(types = Service09.class)
93-
static class BaseTestCase implements TestInterface08 {
93+
class BaseTestCase implements TestInterface08 {
9494

9595
@Autowired
9696
Service08 service08;

0 commit comments

Comments
 (0)