diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideMetadata.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideMetadata.java new file mode 100644 index 0000000000..d32be05eee --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideMetadata.java @@ -0,0 +1,93 @@ +/* + * 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 java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Objects; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.core.ResolvableType; +import org.springframework.lang.Nullable; +import org.springframework.test.context.bean.override.BeanOverrideStrategy; +import org.springframework.test.context.bean.override.OverrideMetadata; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.StringUtils; + +/** + * {@link OverrideMetadata} implementation for {@link TestBean}. + * + * @author Simon Baslé + * @author Stephane Nicoll + * @since 6.2 + */ +final class TestBeanOverrideMetadata extends OverrideMetadata { + + private final Method overrideMethod; + + private final String beanName; + + TestBeanOverrideMetadata(Field field, Method overrideMethod, TestBean overrideAnnotation, + ResolvableType typeToOverride) { + + super(field, typeToOverride, BeanOverrideStrategy.REPLACE_DEFINITION); + this.beanName = overrideAnnotation.name(); + this.overrideMethod = overrideMethod; + } + + @Override + @Nullable + protected String getBeanName() { + return StringUtils.hasText(this.beanName) ? this.beanName : super.getBeanName(); + } + + @Override + protected Object createOverride(String beanName, @Nullable BeanDefinition existingBeanDefinition, + @Nullable Object existingBeanInstance) { + + try { + ReflectionUtils.makeAccessible(this.overrideMethod); + return this.overrideMethod.invoke(null); + } + catch (IllegalAccessException | InvocationTargetException ex) { + throw new IllegalStateException("Failed to invoke bean overriding method " + this.overrideMethod.getName() + + "; a static method with no formal parameters is expected", ex); + } + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + TestBeanOverrideMetadata that = (TestBeanOverrideMetadata) o; + return Objects.equals(this.overrideMethod, that.overrideMethod) + && Objects.equals(this.beanName, that.beanName); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), this.overrideMethod, this.beanName); + } +} 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 5e28e57c7a..e7a92c0022 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 @@ -18,25 +18,18 @@ package org.springframework.test.context.bean.override.convention; import java.lang.annotation.Annotation; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; -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; import org.springframework.test.context.bean.override.BeanOverrideProcessor; -import org.springframework.test.context.bean.override.BeanOverrideStrategy; -import org.springframework.test.context.bean.override.OverrideMetadata; import org.springframework.util.Assert; -import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils.MethodFilter; import org.springframework.util.StringUtils; @@ -143,61 +136,4 @@ class TestBeanOverrideProcessor implements BeanOverrideProcessor { return methods; } - - static final class TestBeanOverrideMetadata extends OverrideMetadata { - - private final Method overrideMethod; - - private final String beanName; - - public TestBeanOverrideMetadata(Field field, Method overrideMethod, TestBean overrideAnnotation, - ResolvableType typeToOverride) { - - super(field, typeToOverride, BeanOverrideStrategy.REPLACE_DEFINITION); - this.beanName = overrideAnnotation.name(); - this.overrideMethod = overrideMethod; - } - - @Override - @Nullable - protected String getBeanName() { - return StringUtils.hasText(this.beanName) ? this.beanName : super.getBeanName(); - } - - @Override - protected Object createOverride(String beanName, @Nullable BeanDefinition existingBeanDefinition, - @Nullable Object existingBeanInstance) { - - try { - ReflectionUtils.makeAccessible(this.overrideMethod); - return this.overrideMethod.invoke(null); - } - catch (IllegalAccessException | InvocationTargetException ex) { - throw new IllegalStateException("Failed to invoke bean overriding method " + this.overrideMethod.getName() + - "; a static method with no formal parameters is expected", ex); - } - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - if (!super.equals(o)) { - return false; - } - TestBeanOverrideMetadata that = (TestBeanOverrideMetadata) o; - return Objects.equals(this.overrideMethod, that.overrideMethod) - && Objects.equals(this.beanName, that.beanName); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), this.overrideMethod, this.beanName); - } - } - } diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanMetadata.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideMetadata.java similarity index 89% rename from spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanMetadata.java rename to spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideMetadata.java index b1f9995800..69dae343b8 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanMetadata.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * 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. @@ -31,6 +31,7 @@ import org.springframework.core.ResolvableType; import org.springframework.core.style.ToStringCreator; import org.springframework.lang.Nullable; import org.springframework.test.context.bean.override.BeanOverrideStrategy; +import org.springframework.test.context.bean.override.OverrideMetadata; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; @@ -39,12 +40,12 @@ import org.springframework.util.StringUtils; import static org.mockito.Mockito.mock; /** - * A complete definition that can be used to create a Mockito mock. + * {@link OverrideMetadata} implementation for Mockito {@code mock} support. * * @author Phillip Webb * @since 6.2 */ -class MockitoBeanMetadata extends MockitoMetadata { +class MockitoBeanOverrideMetadata extends MockitoOverrideMetadata { private final Set> extraInterfaces; @@ -53,12 +54,12 @@ class MockitoBeanMetadata extends MockitoMetadata { private final boolean serializable; - MockitoBeanMetadata(MockitoBean annotation, Field field, ResolvableType typeToMock) { + MockitoBeanOverrideMetadata(MockitoBean annotation, Field field, ResolvableType typeToMock) { this(annotation.name(), annotation.reset(), field, typeToMock, annotation.extraInterfaces(), annotation.answers(), annotation.serializable()); } - MockitoBeanMetadata(String name, MockReset reset, Field field, ResolvableType typeToMock, + MockitoBeanOverrideMetadata(String name, MockReset reset, Field field, ResolvableType typeToMock, Class[] extraInterfaces, @Nullable Answers answer, boolean serializable) { super(name, reset, false, field, typeToMock, BeanOverrideStrategy.REPLACE_OR_CREATE_DEFINITION); @@ -68,18 +69,6 @@ class MockitoBeanMetadata extends MockitoMetadata { this.serializable = serializable; } - @Override - protected Object createOverride(String beanName, @Nullable BeanDefinition existingBeanDefinition, @Nullable Object existingBeanInstance) { - return createMock(beanName); - } - - private Set> asClassSet(@Nullable Class[] classes) { - Set> classSet = new LinkedHashSet<>(); - if (classes != null) { - classSet.addAll(Arrays.asList(classes)); - } - return Collections.unmodifiableSet(classSet); - } /** * Return the extra interfaces. @@ -105,6 +94,19 @@ class MockitoBeanMetadata extends MockitoMetadata { return this.serializable; } + @Override + protected Object createOverride(String beanName, @Nullable BeanDefinition existingBeanDefinition, @Nullable Object existingBeanInstance) { + return createMock(beanName); + } + + private Set> asClassSet(@Nullable Class[] classes) { + Set> classSet = new LinkedHashSet<>(); + if (classes != null) { + classSet.addAll(Arrays.asList(classes)); + } + return Collections.unmodifiableSet(classSet); + } + @Override public boolean equals(@Nullable Object obj) { if (obj == this) { @@ -113,7 +115,7 @@ class MockitoBeanMetadata extends MockitoMetadata { if (obj == null || obj.getClass() != getClass()) { return false; } - MockitoBeanMetadata other = (MockitoBeanMetadata) obj; + MockitoBeanOverrideMetadata other = (MockitoBeanOverrideMetadata) obj; boolean result = super.equals(obj); result = result && ObjectUtils.nullSafeEquals(this.extraInterfaces, other.extraInterfaces); result = result && ObjectUtils.nullSafeEquals(this.answer, other.answer); diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideProcessor.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideProcessor.java index 12dc919b51..0344a921f1 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideProcessor.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideProcessor.java @@ -34,12 +34,12 @@ import org.springframework.test.context.bean.override.BeanOverrideProcessor; class MockitoBeanOverrideProcessor implements BeanOverrideProcessor { @Override - public MockitoMetadata createMetadata(Annotation overrideAnnotation, Class testClass, Field field) { + public MockitoOverrideMetadata createMetadata(Annotation overrideAnnotation, Class testClass, Field field) { if (overrideAnnotation instanceof MockitoBean mockBean) { - return new MockitoBeanMetadata(mockBean, field, ResolvableType.forField(field, testClass)); + return new MockitoBeanOverrideMetadata(mockBean, field, ResolvableType.forField(field, testClass)); } else if (overrideAnnotation instanceof MockitoSpyBean spyBean) { - return new MockitoSpyBeanMetadata(spyBean, field, ResolvableType.forField(field, testClass)); + return new MockitoSpyBeanOverrideMetadata(spyBean, field, ResolvableType.forField(field, testClass)); } throw new IllegalStateException(String.format("Invalid annotation passed to MockitoBeanOverrideProcessor: " + "expected @MockitoBean/@MockitoSpyBean on field %s.%s", diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoMetadata.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoOverrideMetadata.java similarity index 89% rename from spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoMetadata.java rename to spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoOverrideMetadata.java index aa69db7855..c4296a214f 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoMetadata.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoOverrideMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * 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. @@ -29,12 +29,12 @@ import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** - * Base class for Mockito override metadata. + * Base {@link OverrideMetadata} implementation for Mockito. * * @author Phillip Webb * @since 6.2 */ -abstract class MockitoMetadata extends OverrideMetadata { +abstract class MockitoOverrideMetadata extends OverrideMetadata { protected final String name; @@ -43,7 +43,7 @@ abstract class MockitoMetadata extends OverrideMetadata { private final boolean proxyTargetAware; - MockitoMetadata(String name, @Nullable MockReset reset, boolean proxyTargetAware, Field field, + MockitoOverrideMetadata(String name, @Nullable MockReset reset, boolean proxyTargetAware, Field field, ResolvableType typeToOverride, BeanOverrideStrategy strategy) { super(field, typeToOverride, strategy); @@ -59,22 +59,6 @@ abstract class MockitoMetadata extends OverrideMetadata { return StringUtils.hasText(this.name) ? this.name : super.getBeanName(); } - @Override - protected void track(Object mock, SingletonBeanRegistry trackingBeanRegistry) { - MockitoBeans tracker = null; - try { - tracker = (MockitoBeans) trackingBeanRegistry.getSingleton(MockitoBeans.class.getName()); - } - catch (NoSuchBeanDefinitionException ignored) { - - } - if (tracker == null) { - tracker= new MockitoBeans(); - trackingBeanRegistry.registerSingleton(MockitoBeans.class.getName(), tracker); - } - tracker.add(mock); - } - /** * Return the mock reset mode. * @return the reset mode @@ -91,6 +75,22 @@ abstract class MockitoMetadata extends OverrideMetadata { return this.proxyTargetAware; } + @Override + protected void track(Object mock, SingletonBeanRegistry trackingBeanRegistry) { + MockitoBeans tracker = null; + try { + tracker = (MockitoBeans) trackingBeanRegistry.getSingleton(MockitoBeans.class.getName()); + } + catch (NoSuchBeanDefinitionException ignored) { + + } + if (tracker == null) { + tracker= new MockitoBeans(); + trackingBeanRegistry.registerSingleton(MockitoBeans.class.getName(), tracker); + } + tracker.add(mock); + } + @Override public boolean equals(@Nullable Object obj) { if (obj == this) { @@ -99,7 +99,7 @@ abstract class MockitoMetadata extends OverrideMetadata { if (obj == null || !getClass().isAssignableFrom(obj.getClass())) { return false; } - MockitoMetadata other = (MockitoMetadata) obj; + MockitoOverrideMetadata other = (MockitoOverrideMetadata) obj; boolean result = super.equals(obj); result = result && ObjectUtils.nullSafeEquals(this.name, other.name); result = result && ObjectUtils.nullSafeEquals(this.reset, other.reset); diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanMetadata.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideMetadata.java similarity index 89% rename from spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanMetadata.java rename to spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideMetadata.java index 1a8b27e7f5..901fc21705 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanMetadata.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * 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. @@ -40,20 +40,20 @@ import org.springframework.util.StringUtils; import static org.mockito.Mockito.mock; /** - * {@link OverrideMetadata} for Mockito {@code spy} support. + * {@link OverrideMetadata} implementation for Mockito {@code spy} support. * * @author Phillip Webb * @author Simon Baslé * @since 6.2 */ -class MockitoSpyBeanMetadata extends MockitoMetadata { +class MockitoSpyBeanOverrideMetadata extends MockitoOverrideMetadata { - MockitoSpyBeanMetadata(MockitoSpyBean spyAnnotation, Field field, ResolvableType typeToSpy) { + MockitoSpyBeanOverrideMetadata(MockitoSpyBean spyAnnotation, Field field, ResolvableType typeToSpy) { this(spyAnnotation.name(), spyAnnotation.reset(), spyAnnotation.proxyTargetAware(), field, typeToSpy); } - MockitoSpyBeanMetadata(String name, MockReset reset, boolean proxyTargetAware, Field field, ResolvableType typeToSpy) { + MockitoSpyBeanOverrideMetadata(String name, MockReset reset, boolean proxyTargetAware, Field field, ResolvableType typeToSpy) { super(name, reset, proxyTargetAware, field, typeToSpy, BeanOverrideStrategy.WRAP_BEAN); Assert.notNull(typeToSpy, "typeToSpy must not be null"); } @@ -68,33 +68,6 @@ class MockitoSpyBeanMetadata extends MockitoMetadata { return createSpy(beanName, existingBeanInstance); } - @Override - public boolean equals(@Nullable Object obj) { - if (obj == this) { - return true; - } - // For SpyBean we want the class to be exactly the same. - if (obj == null || obj.getClass() != getClass()) { - return false; - } - MockitoSpyBeanMetadata that = (MockitoSpyBeanMetadata) obj; - return (super.equals(obj) && ObjectUtils.nullSafeEquals(getBeanType(), that.getBeanType())); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), getBeanType()); - } - - @Override - public String toString() { - return new ToStringCreator(this) - .append("beanName", getBeanName()) - .append("beanType", getBeanType()) - .append("reset", getReset()) - .toString(); - } - @SuppressWarnings("unchecked") T createSpy(String name, Object instance) { Assert.notNull(instance, "Instance must not be null"); @@ -124,6 +97,34 @@ class MockitoSpyBeanMetadata extends MockitoMetadata { return (T) mock(toSpy, settings); } + @Override + public boolean equals(@Nullable Object obj) { + if (obj == this) { + return true; + } + // For SpyBean we want the class to be exactly the same. + if (obj == null || obj.getClass() != getClass()) { + return false; + } + MockitoSpyBeanOverrideMetadata that = (MockitoSpyBeanOverrideMetadata) obj; + return (super.equals(obj) && ObjectUtils.nullSafeEquals(getBeanType(), that.getBeanType())); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), getBeanType()); + } + + @Override + public String toString() { + return new ToStringCreator(this) + .append("beanName", getBeanName()) + .append("beanType", getBeanType()) + .append("reset", getReset()) + .toString(); + } + + /** * A {@link VerificationStartedListener} that bypasses any proxy created by * Spring AOP when the verification of a spy starts. @@ -134,7 +135,6 @@ class MockitoSpyBeanMetadata extends MockitoMetadata { public void onVerificationStarted(VerificationStartedEvent event) { event.setMock(AopTestUtils.getUltimateTargetObject(event.getMock())); } - } } diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideProcessorTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideProcessorTests.java index 19e8ff5a67..b9f2ecd398 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideProcessorTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanOverrideProcessorTests.java @@ -24,7 +24,6 @@ import org.junit.jupiter.api.Test; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.lang.NonNull; -import org.springframework.test.context.bean.override.convention.TestBeanOverrideProcessor.TestBeanOverrideMetadata; import org.springframework.test.context.bean.override.example.ExampleService; import org.springframework.util.ReflectionUtils; diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideProcessorTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideProcessorTests.java index 6fe90daaf4..bd4c0fc49f 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideProcessorTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideProcessorTests.java @@ -40,7 +40,7 @@ public class MockitoBeanOverrideProcessorTests { OverrideMetadata object = processor.createMetadata(annotation, clazz, field); - assertThat(object).isExactlyInstanceOf(MockitoBeanMetadata.class); + assertThat(object).isExactlyInstanceOf(MockitoBeanOverrideMetadata.class); } @Test @@ -52,7 +52,7 @@ public class MockitoBeanOverrideProcessorTests { OverrideMetadata object = processor.createMetadata(annotation, clazz, field); - assertThat(object).isExactlyInstanceOf(MockitoSpyBeanMetadata.class); + assertThat(object).isExactlyInstanceOf(MockitoSpyBeanOverrideMetadata.class); } @Test