diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideProcessor.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideProcessor.java index 71c6dc4787b..68cecbae8c5 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideProcessor.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideProcessor.java @@ -28,6 +28,7 @@ import java.util.Objects; import java.util.Set; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.core.MethodIntrospector; import org.springframework.core.ResolvableType; import org.springframework.lang.Nullable; import org.springframework.test.context.TestContextAnnotationUtils; @@ -88,24 +89,23 @@ class TestBeanOverrideProcessor implements BeanOverrideProcessor { supportedNames.contains(method.getName()) && methodReturnType.isAssignableFrom(method.getReturnType())); - List methods = findMethods(clazz, methodFilter); + Set methods = findMethods(clazz, methodFilter); if (methods.isEmpty() && TestContextAnnotationUtils.searchEnclosingClass(clazz)) { methods = findMethods(clazz.getEnclosingClass(), methodFilter); } - Assert.state(!methods.isEmpty(), () -> """ + int methodCount = methods.size(); + Assert.state(methodCount > 0, () -> """ Failed to find a static test bean factory method in %s with return type %s \ whose name matches one of the supported candidates %s""".formatted( clazz.getName(), methodReturnType.getName(), supportedNames)); - long nameCount = methods.stream().map(Method::getName).distinct().count(); - int methodCount = methods.size(); - Assert.state(nameCount == 1, () -> """ + Assert.state(methodCount == 1, () -> """ Found %d competing static test bean factory methods in %s with return type %s \ whose name matches one of the supported candidates %s""".formatted( methodCount, clazz.getName(), methodReturnType.getName(), supportedNames)); - return methods.get(0); + return methods.iterator().next(); } @Override @@ -138,10 +138,8 @@ class TestBeanOverrideProcessor implements BeanOverrideProcessor { } - private static List findMethods(Class clazz, MethodFilter methodFilter) { - List methods = new ArrayList<>(); - ReflectionUtils.doWithMethods(clazz, methods::add, methodFilter); - return methods; + private static Set findMethods(Class clazz, MethodFilter methodFilter) { + return MethodIntrospector.selectMethods(clazz, methodFilter); } diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanFactory.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanFactory.java new file mode 100644 index 00000000000..eeeb05d07c7 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002-2024 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; + +/** + * Shared {@link TestBean @TestBean} factory methods. + * + * @author Sam Brannen + * @since 6.2 + */ +interface TestBeanFactory { + + public static String createTestMessage() { + return "test"; + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanInterfaceIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanInterfaceIntegrationTests.java new file mode 100644 index 00000000000..95e7bbdc079 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanInterfaceIntegrationTests.java @@ -0,0 +1,56 @@ +/* + * Copyright 2002-2024 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; + +import org.junit.jupiter.api.Test; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * {@link TestBean @TestBean} integration tests for test bean factory methods + * defined in implemented interfaces. + * + * @author Sam Brannen + * @since 6.2 + */ +@SpringJUnitConfig +public class TestBeanInterfaceIntegrationTests implements TestBeanFactory { + + @TestBean(methodName = "createTestMessage") + String message; + + + @Test + void test() { + assertThat(message).isEqualTo("test"); + } + + + @Configuration + static class Config { + + @Bean + String message() { + return "prod"; + } + } + +}