Browse Source
Prior to this commit, bean overrides (such as @MockitoBean, etc.) were discovered multiple times if they were declared: - at the type-level on an interface that is implemented at more than one level in the type hierarchy, the enclosing class hierarchy, or a combination of the type and enclosing class hierarchies. or - on a field declared in a class which can be reached multiple times while traversing the type and enclosing class hierarchies in scenarios such as the following: the class (X) in which the field is declared is a supertype of an enclosing type of the test class, and X is also an enclosing type of a supertype of the test class. Such scenarios resulted in an IllegalStateException stating that a duplicate BeanOverrideHandler was discovered. To address that, this commit revises the search algorithm in BeanOverrideHandler so that all types (superclasses, enclosing classes, and implemented interfaces) are only visited once while traversing the type and enclosing class hierarchies in search of bean override handlers. See gh-33925 See gh-34324 Closes gh-34844pull/35405/head
5 changed files with 211 additions and 10 deletions
@ -0,0 +1,69 @@
@@ -0,0 +1,69 @@
|
||||
/* |
||||
* 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; |
||||
|
||||
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.bean.override.example.ExampleService; |
||||
import org.springframework.test.context.junit.jupiter.SpringExtension; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.springframework.test.mockito.MockitoAssertions.assertIsMock; |
||||
|
||||
/** |
||||
* Abstract top-level class and abstract inner class for integration tests for |
||||
* {@link MockitoBean @MockitoBean} which verify that {@code @MockitoBean} fields |
||||
* are not discovered more than once when searching intertwined enclosing class
|
||||
* hierarchies and type hierarchies, when a superclass is <em>present</em> twice |
||||
* in the intertwined hierarchies. |
||||
* |
||||
* @author Sam Brannen |
||||
* @since 6.2.7 |
||||
* @see MockitoBeanNestedAndTypeHierarchiesWithSuperclassPresentTwiceTests |
||||
* @see <a href="https://github.com/spring-projects/spring-framework/issues/34844">gh-34844</a> |
||||
*/ |
||||
@ExtendWith(SpringExtension.class) |
||||
abstract class AbstractMockitoBeanNestedAndTypeHierarchiesWithSuperclassPresentTwiceTests { |
||||
|
||||
@Autowired |
||||
ApplicationContext enclosingContext; |
||||
|
||||
@MockitoBean |
||||
ExampleService service; |
||||
|
||||
|
||||
@Test |
||||
void topLevelTest() { |
||||
assertIsMock(service); |
||||
assertThat(enclosingContext.getBeanNamesForType(ExampleService.class)).hasSize(1); |
||||
} |
||||
|
||||
|
||||
abstract class AbstractBaseClassForNestedTests { |
||||
|
||||
@Test |
||||
void nestedTest(ApplicationContext nestedContext) { |
||||
assertIsMock(service); |
||||
assertThat(enclosingContext).isSameAs(nestedContext); |
||||
assertThat(enclosingContext.getBeanNamesForType(ExampleService.class)).hasSize(1); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,59 @@
@@ -0,0 +1,59 @@
|
||||
/* |
||||
* 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; |
||||
|
||||
import org.junit.jupiter.api.Nested; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Integration tests for {@link MockitoBean @MockitoBean} which verify that |
||||
* {@code @MockitoBean} fields are not discovered more than once when searching |
||||
* intertwined enclosing class hierarchies and type hierarchies, when a superclass |
||||
* is <em>present</em> twice in the intertwined hierarchies. |
||||
* |
||||
* @author Sam Brannen |
||||
* @since 6.2.7 |
||||
* @see MockitoBeanNestedAndTypeHierarchiesWithEnclosingClassPresentTwiceTests |
||||
* @see MockitoBeanWithInterfacePresentTwiceTests |
||||
* @see <a href="https://github.com/spring-projects/spring-framework/issues/34844">gh-34844</a> |
||||
*/ |
||||
class MockitoBeanNestedAndTypeHierarchiesWithSuperclassPresentTwiceTests |
||||
extends AbstractMockitoBeanNestedAndTypeHierarchiesWithSuperclassPresentTwiceTests { |
||||
|
||||
@Test |
||||
@Override |
||||
void topLevelTest() { |
||||
super.topLevelTest(); |
||||
|
||||
// The following are prerequisites for the reported regression.
|
||||
assertThat(NestedTests.class.getSuperclass()) |
||||
.isEqualTo(AbstractBaseClassForNestedTests.class); |
||||
assertThat(NestedTests.class.getEnclosingClass()) |
||||
.isEqualTo(getClass()); |
||||
assertThat(NestedTests.class.getEnclosingClass().getSuperclass()) |
||||
.isEqualTo(AbstractBaseClassForNestedTests.class.getEnclosingClass()) |
||||
.isEqualTo(getClass().getSuperclass()); |
||||
} |
||||
|
||||
|
||||
@Nested |
||||
class NestedTests extends AbstractBaseClassForNestedTests { |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,66 @@
@@ -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; |
||||
|
||||
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.bean.override.example.ExampleService; |
||||
import org.springframework.test.context.junit.jupiter.SpringExtension; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.springframework.test.mockito.MockitoAssertions.assertIsMock; |
||||
|
||||
/** |
||||
* Integration tests for {@link MockitoBean @MockitoBean} which verify that type-level |
||||
* {@code @MockitoBean} declarations are not discovered more than once when searching |
||||
* a type hierarchy, when an interface is <em>present</em> twice in the hierarchy. |
||||
* |
||||
* @author Sam Brannen |
||||
* @since 6.2.7 |
||||
* @see MockitoBeanNestedAndTypeHierarchiesWithEnclosingClassPresentTwiceTests |
||||
* @see MockitoBeanNestedAndTypeHierarchiesWithSuperclassPresentTwiceTests |
||||
* @see <a href="https://github.com/spring-projects/spring-framework/issues/34844">gh-34844</a> |
||||
*/ |
||||
class MockitoBeanWithInterfacePresentTwiceTests extends AbstractMockitoBeanWithInterfacePresentTwiceTests |
||||
implements MockConfigInterface { |
||||
|
||||
@Test |
||||
void test(ApplicationContext context) { |
||||
assertIsMock(service); |
||||
assertThat(context.getBeanNamesForType(ExampleService.class)).hasSize(1); |
||||
|
||||
// The following are prerequisites for the tested scenario.
|
||||
assertThat(getClass().getInterfaces()).containsExactly(MockConfigInterface.class); |
||||
assertThat(getClass().getSuperclass().getInterfaces()).containsExactly(MockConfigInterface.class); |
||||
} |
||||
|
||||
} |
||||
|
||||
@MockitoBean(types = ExampleService.class) |
||||
interface MockConfigInterface { |
||||
} |
||||
|
||||
@ExtendWith(SpringExtension.class) |
||||
abstract class AbstractMockitoBeanWithInterfacePresentTwiceTests implements MockConfigInterface { |
||||
|
||||
@Autowired |
||||
ExampleService service; |
||||
|
||||
} |
||||
Loading…
Reference in new issue