Skip to content

Commit

Permalink
Polish bean override internals
Browse files Browse the repository at this point in the history
  • Loading branch information
sbrannen committed Sep 18, 2024
1 parent c832d5f commit 6c2cba5
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,21 @@
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;

/**
* {@link OverrideMetadata} implementation for Mockito {@code mock} support.
*
* @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;

Expand All @@ -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);
}

Expand All @@ -90,12 +91,12 @@ Set<Class<?>> getExtraInterfaces() {
* 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() {
Expand All @@ -108,15 +109,15 @@ protected Object createOverride(String beanName, @Nullable BeanDefinition existi
}

@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);
}
if (!this.extraInterfaces.isEmpty()) {
settings.extraInterfaces(ClassUtils.toClassArray(this.extraInterfaces));
}
settings.defaultAnswer(this.answer);
settings.defaultAnswer(this.answers);
if (this.serializable) {
settings.serializable();
}
Expand All @@ -132,27 +133,26 @@ public boolean equals(@Nullable Object other) {
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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<>();

Expand All @@ -35,9 +37,8 @@ void add(Object bean) {
this.beans.add(bean);
}

@Override
public Iterator<Object> iterator() {
return this.beans.iterator();
void resetAll() {
this.beans.forEach(Mockito::reset);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,17 @@
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 {
Expand All @@ -45,7 +46,7 @@ protected MockitoOverrideMetadata(Field field, ResolvableType beanType, @Nullabl
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;
}

Expand Down Expand Up @@ -73,10 +74,9 @@ protected void track(Object mock, SingletonBeanRegistry trackingBeanRegistry) {
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);
Expand All @@ -87,19 +87,25 @@ public boolean equals(@Nullable Object other) {
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();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,7 @@ private void resetMocks(ConfigurableApplicationContext applicationContext, MockR
}
}
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@

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;
Expand Down Expand Up @@ -68,8 +67,7 @@ protected Object createOverride(String beanName, @Nullable BeanDefinition existi
}

@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);
Expand All @@ -96,15 +94,6 @@ <T> T createSpy(String name, Object instance) {
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand All @@ -91,18 +83,11 @@ void fieldIsMockedWhenNoOriginalBean(ApplicationContext ctx) {
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);
}

Expand Down

0 comments on commit 6c2cba5

Please sign in to comment.