Polish bean override internals

This commit is contained in:
Sam Brannen 2024-09-18 10:09:22 +02:00
parent c832d5f496
commit 6c2cba5d8a
6 changed files with 54 additions and 78 deletions

View File

@ -35,7 +35,6 @@ 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;
import org.springframework.util.StringUtils;
/**
@ -43,13 +42,14 @@ import org.springframework.util.StringUtils;
*
* @author Phillip Webb
* @author Stephane Nicoll
* @author Sam Brannen
* @since 6.2
*/
class MockitoBeanOverrideMetadata extends MockitoOverrideMetadata {
private final Set<Class<?>> extraInterfaces;
private final Answers answer;
private final Answers answers;
private final boolean serializable;
@ -59,21 +59,22 @@ class MockitoBeanOverrideMetadata extends MockitoOverrideMetadata {
annotation.reset(), annotation.extraInterfaces(), annotation.answers(), annotation.serializable());
}
MockitoBeanOverrideMetadata(Field field, ResolvableType typeToMock, @Nullable String beanName, MockReset reset,
Class<?>[] extraInterfaces, @Nullable Answers answer, boolean serializable) {
private MockitoBeanOverrideMetadata(Field field, ResolvableType typeToMock, @Nullable String beanName, MockReset reset,
Class<?>[] extraInterfaces, @Nullable Answers answers, boolean serializable) {
super(field, typeToMock, beanName, BeanOverrideStrategy.REPLACE_OR_CREATE_DEFINITION, reset, false);
Assert.notNull(typeToMock, "'typeToMock' must not be null");
this.extraInterfaces = asClassSet(extraInterfaces);
this.answer = (answer != null) ? answer : Answers.RETURNS_DEFAULTS;
this.answers = (answers != null ? answers : Answers.RETURNS_DEFAULTS);
this.serializable = serializable;
}
private static Set<Class<?>> asClassSet(@Nullable Class<?>[] classes) {
Set<Class<?>> classSet = new LinkedHashSet<>();
if (classes != null) {
classSet.addAll(Arrays.asList(classes));
private static Set<Class<?>> asClassSet(Class<?>[] classes) {
if (classes.length == 0) {
return Collections.emptySet();
}
Set<Class<?>> classSet = new LinkedHashSet<>(Arrays.asList(classes));
return Collections.unmodifiableSet(classSet);
}
@ -90,12 +91,12 @@ class MockitoBeanOverrideMetadata extends MockitoOverrideMetadata {
* Return the {@link Answers}.
* @return the answers mode
*/
Answers getAnswer() {
return this.answer;
Answers getAnswers() {
return this.answers;
}
/**
* Return if the mock is serializable.
* Determine if the mock is serializable.
* @return {@code true} if the mock is serializable
*/
boolean isSerializable() {
@ -108,7 +109,7 @@ class MockitoBeanOverrideMetadata extends MockitoOverrideMetadata {
}
@SuppressWarnings("unchecked")
<T> T createMock(String name) {
private <T> T createMock(String name) {
MockSettings settings = MockReset.withSettings(getReset());
if (StringUtils.hasLength(name)) {
settings.name(name);
@ -116,7 +117,7 @@ class MockitoBeanOverrideMetadata extends MockitoOverrideMetadata {
if (!this.extraInterfaces.isEmpty()) {
settings.extraInterfaces(ClassUtils.toClassArray(this.extraInterfaces));
}
settings.defaultAnswer(this.answer);
settings.defaultAnswer(this.answers);
if (this.serializable) {
settings.serializable();
}
@ -132,27 +133,26 @@ class MockitoBeanOverrideMetadata extends MockitoOverrideMetadata {
if (other == null || other.getClass() != getClass()) {
return false;
}
MockitoBeanOverrideMetadata that = (MockitoBeanOverrideMetadata) other;
boolean result = super.equals(that);
result = result && ObjectUtils.nullSafeEquals(this.extraInterfaces, that.extraInterfaces);
result = result && ObjectUtils.nullSafeEquals(this.answer, that.answer);
result = result && this.serializable == that.serializable;
return result;
return (other instanceof MockitoBeanOverrideMetadata that && super.equals(that) &&
(this.serializable == that.serializable) && (this.answers == that.answers) &&
Objects.equals(this.extraInterfaces, that.extraInterfaces));
}
@Override
public int hashCode() {
return Objects.hash(this.extraInterfaces, this.answer, this.serializable) + super.hashCode();
return super.hashCode() + Objects.hash(this.extraInterfaces, this.answers, this.serializable);
}
@Override
public String toString() {
return new ToStringCreator(this)
.append("field", getField())
.append("beanType", getBeanType())
.append("beanName", getBeanName())
.append("strategy", getStrategy())
.append("reset", getReset())
.append("extraInterfaces", getExtraInterfaces())
.append("answer", getAnswer())
.append("answers", getAnswers())
.append("serializable", isSerializable())
.toString();
}

View File

@ -17,16 +17,18 @@
package org.springframework.test.context.bean.override.mockito;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.mockito.Mockito;
/**
* Beans created using Mockito.
*
* @author Andy Wilkinson
* @author Sam Brannen
* @since 6.2
*/
class MockitoBeans implements Iterable<Object> {
class MockitoBeans {
private final List<Object> beans = new ArrayList<>();
@ -35,9 +37,8 @@ class MockitoBeans implements Iterable<Object> {
this.beans.add(bean);
}
@Override
public Iterator<Object> iterator() {
return this.beans.iterator();
void resetAll() {
this.beans.forEach(Mockito::reset);
}
}

View File

@ -22,16 +22,17 @@ import java.util.Objects;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
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.ObjectUtils;
/**
* Base {@link OverrideMetadata} implementation for Mockito.
*
* @author Phillip Webb
* @author Stephane Nicoll
* @author Sam Brannen
* @since 6.2
*/
abstract class MockitoOverrideMetadata extends OverrideMetadata {
@ -45,7 +46,7 @@ abstract class MockitoOverrideMetadata extends OverrideMetadata {
BeanOverrideStrategy strategy, @Nullable MockReset reset, boolean proxyTargetAware) {
super(field, beanType, beanName, strategy);
this.reset = (reset != null) ? reset : MockReset.AFTER;
this.reset = (reset != null ? reset : MockReset.AFTER);
this.proxyTargetAware = proxyTargetAware;
}
@ -73,10 +74,9 @@ abstract class MockitoOverrideMetadata extends OverrideMetadata {
tracker = (MockitoBeans) trackingBeanRegistry.getSingleton(MockitoBeans.class.getName());
}
catch (NoSuchBeanDefinitionException ignored) {
}
if (tracker == null) {
tracker= new MockitoBeans();
tracker = new MockitoBeans();
trackingBeanRegistry.registerSingleton(MockitoBeans.class.getName(), tracker);
}
tracker.add(mock);
@ -87,19 +87,25 @@ abstract class MockitoOverrideMetadata extends OverrideMetadata {
if (other == this) {
return true;
}
if (other == null || !getClass().isAssignableFrom(other.getClass())) {
return false;
}
MockitoOverrideMetadata that = (MockitoOverrideMetadata) other;
boolean result = super.equals(that);
result = result && ObjectUtils.nullSafeEquals(this.reset, that.reset);
result = result && ObjectUtils.nullSafeEquals(this.proxyTargetAware, that.proxyTargetAware);
return result;
return (other instanceof MockitoOverrideMetadata that && super.equals(that) &&
(this.reset == that.reset) && (this.proxyTargetAware == that.proxyTargetAware));
}
@Override
public int hashCode() {
return Objects.hash(this.reset, this.proxyTargetAware) + super.hashCode();
return super.hashCode() + Objects.hash(this.reset, this.proxyTargetAware);
}
@Override
public String toString() {
return new ToStringCreator(this)
.append("field", getField())
.append("beanType", getBeanType())
.append("beanName", getBeanName())
.append("strategy", getStrategy())
.append("reset", getReset())
.append("proxyTargetAware", isProxyTargetAware())
.toString();
}
}

View File

@ -87,12 +87,7 @@ public class MockitoResetTestExecutionListener extends AbstractTestExecutionList
}
}
try {
MockitoBeans mockedBeans = beanFactory.getBean(MockitoBeans.class);
for (Object mockedBean : mockedBeans) {
if (reset.equals(MockReset.get(mockedBean))) {
Mockito.reset(mockedBean);
}
}
beanFactory.getBean(MockitoBeans.class).resetAll();
}
catch (NoSuchBeanDefinitionException ex) {
// Continue

View File

@ -27,7 +27,6 @@ import org.mockito.listeners.VerificationStartedListener;
import org.springframework.beans.factory.config.BeanDefinition;
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;
@ -68,8 +67,7 @@ class MockitoSpyBeanOverrideMetadata extends MockitoOverrideMetadata {
}
@SuppressWarnings("unchecked")
<T> T createSpy(String name, Object instance) {
Assert.notNull(instance, "Instance must not be null");
private <T> T createSpy(String name, Object instance) {
Class<?> resolvedTypeToOverride = getBeanType().resolve();
Assert.notNull(resolvedTypeToOverride, "Failed to resolve type to override");
Assert.isInstanceOf(resolvedTypeToOverride, instance);
@ -96,15 +94,6 @@ class MockitoSpyBeanOverrideMetadata extends MockitoOverrideMetadata {
return (T) Mockito.mock(toSpy, settings);
}
@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

View File

@ -56,22 +56,14 @@ public class MockitoBeanForByNameLookupIntegrationTests {
@Test
void fieldHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("field"))
.isInstanceOf(ExampleService.class)
.satisfies(o -> assertThat(Mockito.mockingDetails(o).isMock()).as("isMock").isTrue())
.isSameAs(this.field);
assertThat(this.field.greeting()).as("mocked greeting").isNull();
}
@Test
void renamedFieldHasOverride(ApplicationContext ctx) {
void fieldAndRenamedFieldHaveSameOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("field"))
.isInstanceOf(ExampleService.class)
.satisfies(o -> assertThat(Mockito.mockingDetails(o).isMock()).as("isMock").isTrue())
.isSameAs(this.field)
.isSameAs(this.renamed1);
assertThat(this.field.greeting()).as("mocked greeting").isNull();
assertThat(this.renamed1.greeting()).as("mocked greeting").isNull();
}
@ -91,18 +83,11 @@ public class MockitoBeanForByNameLookupIntegrationTests {
public class MockitoBeanNestedTests {
@Test
void fieldHasOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("nestedField"))
.isInstanceOf(ExampleService.class)
.satisfies(o -> assertThat(Mockito.mockingDetails(o).isMock()).as("isMock").isTrue())
.isSameAs(nestedField);
}
@Test
void renamedFieldHasOverride(ApplicationContext ctx) {
void fieldAndRenamedFieldHaveSameOverride(ApplicationContext ctx) {
assertThat(ctx.getBean("nestedField"))
.isInstanceOf(ExampleService.class)
.satisfies(o -> assertThat(Mockito.mockingDetails(o).isMock()).as("isMock").isTrue())
.isSameAs(nestedField)
.isSameAs(renamed2);
}