Make Mock/Spy qualifiers part of context cache key

Refine @MockBean/@SpyBean qualifier support so that qualifiers form part
of the context cache key. Prior to this commit is was possible that two
different tests could accidentally share the same context if they
defined the same @Mock but with different @Qualifiers.

See gh-6753
pull/6919/merge
Phillip Webb 8 years ago
parent 04448d6bd9
commit aad40093ff

@ -16,8 +16,6 @@
package org.springframework.boot.test.mock.mockito; package org.springframework.boot.test.mock.mockito;
import java.lang.reflect.AnnotatedElement;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
/** /**
@ -30,28 +28,20 @@ abstract class Definition {
private static final int MULTIPLIER = 31; private static final int MULTIPLIER = 31;
private final AnnotatedElement element;
private final String name; private final String name;
private final MockReset reset; private final MockReset reset;
private final boolean proxyTargetAware; private final boolean proxyTargetAware;
Definition(AnnotatedElement element, String name, MockReset reset, private final QualifierDefinition qualifier;
boolean proxyTargetAware) {
this.element = element; Definition(String name, MockReset reset, boolean proxyTargetAware,
QualifierDefinition qualifier) {
this.name = name; this.name = name;
this.reset = (reset != null ? reset : MockReset.AFTER); this.reset = (reset != null ? reset : MockReset.AFTER);
this.proxyTargetAware = proxyTargetAware; this.proxyTargetAware = proxyTargetAware;
} this.qualifier = qualifier;
/**
* Return the {@link AnnotatedElement} that holds this definition.
* @return the element that defines this definition or {@code null}
*/
public AnnotatedElement getElement() {
return this.element;
} }
/** /**
@ -78,6 +68,14 @@ abstract class Definition {
return this.proxyTargetAware; return this.proxyTargetAware;
} }
/**
* Return the qualifier or {@code null}.
* @return the qualifier
*/
public QualifierDefinition getQualifier() {
return this.qualifier;
}
@Override @Override
public int hashCode() { public int hashCode() {
int result = 1; int result = 1;
@ -85,6 +83,7 @@ abstract class Definition {
result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.reset); result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.reset);
result = MULTIPLIER * result result = MULTIPLIER * result
+ ObjectUtils.nullSafeHashCode(this.proxyTargetAware); + ObjectUtils.nullSafeHashCode(this.proxyTargetAware);
result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.qualifier);
return result; return result;
} }
@ -102,6 +101,7 @@ abstract class Definition {
result &= ObjectUtils.nullSafeEquals(this.reset, other.reset); result &= ObjectUtils.nullSafeEquals(this.reset, other.reset);
result &= ObjectUtils.nullSafeEquals(this.proxyTargetAware, result &= ObjectUtils.nullSafeEquals(this.proxyTargetAware,
other.proxyTargetAware); other.proxyTargetAware);
result &= ObjectUtils.nullSafeEquals(this.qualifier, other.qualifier);
return result; return result;
} }

@ -91,10 +91,11 @@ class DefinitionsParser {
"The name attribute can only be used when mocking a single class"); "The name attribute can only be used when mocking a single class");
} }
for (ResolvableType typeToMock : typesToMock) { for (ResolvableType typeToMock : typesToMock) {
MockDefinition definition = new MockDefinition(element, annotation.name(), MockDefinition definition = new MockDefinition(annotation.name(), typeToMock,
typeToMock, annotation.extraInterfaces(), annotation.answer(), annotation.extraInterfaces(), annotation.answer(),
annotation.serializable(), annotation.reset()); annotation.serializable(), annotation.reset(),
addDefinition(definition, "mock"); QualifierDefinition.forElement(element));
addDefinition(element, definition, "mock");
} }
} }
@ -107,16 +108,17 @@ class DefinitionsParser {
"The name attribute can only be used when spying a single class"); "The name attribute can only be used when spying a single class");
} }
for (ResolvableType typeToSpy : typesToSpy) { for (ResolvableType typeToSpy : typesToSpy) {
SpyDefinition definition = new SpyDefinition(element, annotation.name(), SpyDefinition definition = new SpyDefinition(annotation.name(), typeToSpy,
typeToSpy, annotation.reset(), annotation.proxyTargetAware()); annotation.reset(), annotation.proxyTargetAware(),
addDefinition(definition, "spy"); QualifierDefinition.forElement(element));
addDefinition(element, definition, "spy");
} }
} }
private void addDefinition(Definition definition, String type) { private void addDefinition(AnnotatedElement element, Definition definition,
String type) {
boolean isNewDefinition = this.definitions.add(definition); boolean isNewDefinition = this.definitions.add(definition);
Assert.state(isNewDefinition, "Duplicate " + type + " definition " + definition); Assert.state(isNewDefinition, "Duplicate " + type + " definition " + definition);
AnnotatedElement element = definition.getElement();
if (element instanceof Field) { if (element instanceof Field) {
Field field = (Field) element; Field field = (Field) element;
this.definitionFields.put(definition, field); this.definitionFields.put(definition, field);

@ -16,7 +16,6 @@
package org.springframework.boot.test.mock.mockito; package org.springframework.boot.test.mock.mockito;
import java.lang.reflect.AnnotatedElement;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
@ -50,10 +49,10 @@ class MockDefinition extends Definition {
private final boolean serializable; private final boolean serializable;
MockDefinition(AnnotatedElement element, String name, ResolvableType typeToMock, MockDefinition(String name, ResolvableType typeToMock, Class<?>[] extraInterfaces,
Class<?>[] extraInterfaces, Answers answer, boolean serializable, Answers answer, boolean serializable, MockReset reset,
MockReset reset) { QualifierDefinition qualifier) {
super(element, name, reset, false); super(name, reset, false, qualifier);
Assert.notNull(typeToMock, "TypeToMock must not be null"); Assert.notNull(typeToMock, "TypeToMock must not be null");
this.typeToMock = typeToMock; this.typeToMock = typeToMock;
this.extraInterfaces = asClassSet(extraInterfaces); this.extraInterfaces = asClassSet(extraInterfaces);

@ -17,7 +17,6 @@
package org.springframework.boot.test.mock.mockito; package org.springframework.boot.test.mock.mockito;
import java.beans.PropertyDescriptor; import java.beans.PropertyDescriptor;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -44,7 +43,6 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistry;
@ -209,9 +207,8 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
definition.setFactoryMethodName("createMock"); definition.setFactoryMethodName("createMock");
definition.getConstructorArgumentValues().addIndexedArgumentValue(0, definition.getConstructorArgumentValues().addIndexedArgumentValue(0,
mockDefinition); mockDefinition);
AnnotatedElement element = mockDefinition.getElement(); if (mockDefinition.getQualifier() != null) {
if (element instanceof Field) { mockDefinition.getQualifier().applyTo(definition);
definition.setQualifiedElement(element);
} }
return definition; return definition;
} }
@ -232,17 +229,17 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
if (StringUtils.hasLength(mockDefinition.getName())) { if (StringUtils.hasLength(mockDefinition.getName())) {
return mockDefinition.getName(); return mockDefinition.getName();
} }
String[] existingBeans = findCandidateBeans(beanFactory, mockDefinition); Set<String> existingBeans = findCandidateBeans(beanFactory, mockDefinition);
if (ObjectUtils.isEmpty(existingBeans)) { if (existingBeans.isEmpty()) {
return this.beanNameGenerator.generateBeanName(beanDefinition, registry); return this.beanNameGenerator.generateBeanName(beanDefinition, registry);
} }
if (existingBeans.length == 1) { if (existingBeans.size() == 1) {
return existingBeans[0]; return existingBeans.iterator().next();
} }
throw new IllegalStateException( throw new IllegalStateException(
"Unable to register mock bean " + mockDefinition.getTypeToMock() "Unable to register mock bean " + mockDefinition.getTypeToMock()
+ " expected a single matching bean to replace but found " + " expected a single matching bean to replace but found "
+ new TreeSet<String>(Arrays.asList(existingBeans))); + existingBeans);
} }
private void registerSpy(ConfigurableListableBeanFactory beanFactory, private void registerSpy(ConfigurableListableBeanFactory beanFactory,
@ -256,22 +253,17 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
} }
} }
private String[] findCandidateBeans(ConfigurableListableBeanFactory beanFactory, private Set<String> findCandidateBeans(ConfigurableListableBeanFactory beanFactory,
MockDefinition mockDefinition) { MockDefinition mockDefinition) {
String[] beans = getExistingBeans(beanFactory, mockDefinition.getTypeToMock()); QualifierDefinition qualifier = mockDefinition.getQualifier();
// Attempt to filter using qualifiers Set<String> candidates = new TreeSet<String>();
if (beans.length > 1 && mockDefinition.getElement() instanceof Field) { for (String candidate : getExistingBeans(beanFactory,
DependencyDescriptor descriptor = new DependencyDescriptor( mockDefinition.getTypeToMock())) {
(Field) mockDefinition.getElement(), true); if (qualifier == null || qualifier.matches(beanFactory, candidate)) {
Set<String> candidates = new LinkedHashSet<String>(); candidates.add(candidate);
for (String bean : beans) {
if (beanFactory.isAutowireCandidate(bean, descriptor)) {
candidates.add(bean);
}
} }
return candidates.toArray(new String[candidates.size()]);
} }
return beans; return candidates;
} }
private String[] getExistingBeans(ConfigurableListableBeanFactory beanFactory, private String[] getExistingBeans(ConfigurableListableBeanFactory beanFactory,

@ -0,0 +1,110 @@
/*
* Copyright 2012-2016 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
*
* http://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.boot.test.mock.mockito;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.annotation.AnnotationUtils;
/**
* Definition of a Spring {@link Qualifier @Qualifier}.
*
* @author Phillip Webb
* @author Stephane Nicoll
* @see Definition
*/
class QualifierDefinition {
private final Field field;
private final DependencyDescriptor descriptor;
private final Set<Annotation> annotations;
QualifierDefinition(Field field, Set<Annotation> annotations) {
// We can't use the field or descriptor as part of the context key
// but we can assume that if two fields have the same qualifiers then
// it's safe for Spring to use either for qualifier logic
this.field = field;
this.descriptor = new DependencyDescriptor(field, true);
this.annotations = annotations;
}
public boolean matches(ConfigurableListableBeanFactory beanFactory, String beanName) {
return beanFactory.isAutowireCandidate(beanName, this.descriptor);
}
public void applyTo(RootBeanDefinition definition) {
definition.setQualifiedElement(this.field);
}
@Override
public int hashCode() {
return this.annotations.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || !getClass().isAssignableFrom(obj.getClass())) {
return false;
}
QualifierDefinition other = (QualifierDefinition) obj;
return this.annotations.equals(other.annotations);
}
public static QualifierDefinition forElement(AnnotatedElement element) {
if (element != null && element instanceof Field) {
Field field = (Field) element;
Set<Annotation> annotations = getQualifierAnnotations(field);
if (!annotations.isEmpty()) {
return new QualifierDefinition(field, annotations);
}
}
return null;
}
private static Set<Annotation> getQualifierAnnotations(Field field) {
// Assume that any annotations other than @MockBean/@SpyBean are qualifiers
Annotation[] candidates = field.getDeclaredAnnotations();
Set<Annotation> annotations = new HashSet<Annotation>(candidates.length);
for (Annotation candidate : candidates) {
if (!isMockOrSpyAnnotation(candidate)) {
annotations.add(candidate);
}
}
return annotations;
}
private static boolean isMockOrSpyAnnotation(Annotation candidate) {
Class<? extends Annotation> type = candidate.annotationType();
return (type.equals(MockBean.class) || type.equals(SpyBean.class)
|| AnnotationUtils.isAnnotationMetaPresent(type, MockBean.class)
|| AnnotationUtils.isAnnotationMetaPresent(type, SpyBean.class));
}
}

@ -16,8 +16,6 @@
package org.springframework.boot.test.mock.mockito; package org.springframework.boot.test.mock.mockito;
import java.lang.reflect.AnnotatedElement;
import org.mockito.MockSettings; import org.mockito.MockSettings;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.internal.util.MockUtil; import org.mockito.internal.util.MockUtil;
@ -41,9 +39,9 @@ class SpyDefinition extends Definition {
private final ResolvableType typeToSpy; private final ResolvableType typeToSpy;
SpyDefinition(AnnotatedElement element, String name, ResolvableType typeToSpy, SpyDefinition(String name, ResolvableType typeToSpy, MockReset reset,
MockReset reset, boolean proxyTargetAware) { boolean proxyTargetAware, QualifierDefinition qualifier) {
super(element, name, reset, proxyTargetAware); super(name, reset, proxyTargetAware, qualifier);
Assert.notNull(typeToSpy, "TypeToSpy must not be null"); Assert.notNull(typeToSpy, "TypeToSpy must not be null");
this.typeToSpy = typeToSpy; this.typeToSpy = typeToSpy;

@ -24,6 +24,7 @@ import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import org.mockito.Answers; import org.mockito.Answers;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.mock.mockito.example.ExampleExtraInterface; import org.springframework.boot.test.mock.mockito.example.ExampleExtraInterface;
import org.springframework.boot.test.mock.mockito.example.ExampleService; import org.springframework.boot.test.mock.mockito.example.ExampleService;
import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller; import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller;
@ -67,7 +68,6 @@ public class DefinitionsParserTests {
this.parser.parse(MockBeanAttributes.class); this.parser.parse(MockBeanAttributes.class);
assertThat(getDefinitions()).hasSize(1); assertThat(getDefinitions()).hasSize(1);
MockDefinition definition = getMockDefinition(0); MockDefinition definition = getMockDefinition(0);
assertThat(definition.getElement()).isEqualTo(MockBeanAttributes.class);
assertThat(definition.getName()).isEqualTo("Name"); assertThat(definition.getName()).isEqualTo("Name");
assertThat(definition.getTypeToMock().resolve()).isEqualTo(ExampleService.class); assertThat(definition.getTypeToMock().resolve()).isEqualTo(ExampleService.class);
assertThat(definition.getExtraInterfaces()) assertThat(definition.getExtraInterfaces())
@ -75,6 +75,7 @@ public class DefinitionsParserTests {
assertThat(definition.getAnswer()).isEqualTo(Answers.RETURNS_SMART_NULLS); assertThat(definition.getAnswer()).isEqualTo(Answers.RETURNS_SMART_NULLS);
assertThat(definition.isSerializable()).isEqualTo(true); assertThat(definition.isSerializable()).isEqualTo(true);
assertThat(definition.getReset()).isEqualTo(MockReset.NONE); assertThat(definition.getReset()).isEqualTo(MockReset.NONE);
assertThat(definition.getQualifier()).isNull();
} }
@Test @Test
@ -82,14 +83,15 @@ public class DefinitionsParserTests {
this.parser.parse(MockBeanOnClassAndField.class); this.parser.parse(MockBeanOnClassAndField.class);
assertThat(getDefinitions()).hasSize(2); assertThat(getDefinitions()).hasSize(2);
MockDefinition classDefinition = getMockDefinition(0); MockDefinition classDefinition = getMockDefinition(0);
assertThat(classDefinition.getElement()).isEqualTo(MockBeanOnClassAndField.class);
assertThat(classDefinition.getTypeToMock().resolve()) assertThat(classDefinition.getTypeToMock().resolve())
.isEqualTo(ExampleService.class); .isEqualTo(ExampleService.class);
assertThat(classDefinition.getQualifier()).isNull();
MockDefinition fieldDefinition = getMockDefinition(1); MockDefinition fieldDefinition = getMockDefinition(1);
assertThat(fieldDefinition.getElement()).isEqualTo(
ReflectionUtils.findField(MockBeanOnClassAndField.class, "caller"));
assertThat(fieldDefinition.getTypeToMock().resolve()) assertThat(fieldDefinition.getTypeToMock().resolve())
.isEqualTo(ExampleServiceCaller.class); .isEqualTo(ExampleServiceCaller.class);
QualifierDefinition qualifier = QualifierDefinition.forElement(
ReflectionUtils.findField(MockBeanOnClassAndField.class, "caller"));
assertThat(fieldDefinition.getQualifier()).isNotNull().isEqualTo(qualifier);
} }
@Test @Test
@ -148,11 +150,11 @@ public class DefinitionsParserTests {
this.parser.parse(SpyBeanAttributes.class); this.parser.parse(SpyBeanAttributes.class);
assertThat(getDefinitions()).hasSize(1); assertThat(getDefinitions()).hasSize(1);
SpyDefinition definition = getSpyDefinition(0); SpyDefinition definition = getSpyDefinition(0);
assertThat(definition.getElement()).isEqualTo(SpyBeanAttributes.class);
assertThat(definition.getName()).isEqualTo("Name"); assertThat(definition.getName()).isEqualTo("Name");
assertThat(definition.getTypeToSpy().resolve()) assertThat(definition.getTypeToSpy().resolve())
.isEqualTo(RealExampleService.class); .isEqualTo(RealExampleService.class);
assertThat(definition.getReset()).isEqualTo(MockReset.NONE); assertThat(definition.getReset()).isEqualTo(MockReset.NONE);
assertThat(definition.getQualifier()).isNull();
} }
@Test @Test
@ -160,12 +162,13 @@ public class DefinitionsParserTests {
this.parser.parse(SpyBeanOnClassAndField.class); this.parser.parse(SpyBeanOnClassAndField.class);
assertThat(getDefinitions()).hasSize(2); assertThat(getDefinitions()).hasSize(2);
SpyDefinition classDefinition = getSpyDefinition(0); SpyDefinition classDefinition = getSpyDefinition(0);
assertThat(classDefinition.getElement()).isEqualTo(SpyBeanOnClassAndField.class); assertThat(classDefinition.getQualifier()).isNull();
assertThat(classDefinition.getTypeToSpy().resolve()) assertThat(classDefinition.getTypeToSpy().resolve())
.isEqualTo(RealExampleService.class); .isEqualTo(RealExampleService.class);
SpyDefinition fieldDefinition = getSpyDefinition(1); SpyDefinition fieldDefinition = getSpyDefinition(1);
assertThat(fieldDefinition.getElement()).isEqualTo( QualifierDefinition qualifier = QualifierDefinition.forElement(
ReflectionUtils.findField(SpyBeanOnClassAndField.class, "caller")); ReflectionUtils.findField(SpyBeanOnClassAndField.class, "caller"));
assertThat(fieldDefinition.getQualifier()).isNotNull().isEqualTo(qualifier);
} }
@Test @Test
@ -232,6 +235,7 @@ public class DefinitionsParserTests {
static class MockBeanOnClassAndField { static class MockBeanOnClassAndField {
@MockBean(ExampleServiceCaller.class) @MockBean(ExampleServiceCaller.class)
@Qualifier("test")
private Object caller; private Object caller;
} }
@ -279,6 +283,7 @@ public class DefinitionsParserTests {
static class SpyBeanOnClassAndField { static class SpyBeanOnClassAndField {
@SpyBean(ExampleServiceCaller.class) @SpyBean(ExampleServiceCaller.class)
@Qualifier("test")
private Object caller; private Object caller;
} }

@ -28,6 +28,7 @@ import org.springframework.boot.test.mock.mockito.example.ExampleService;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/** /**
* Tests for {@link MockDefinition}. * Tests for {@link MockDefinition}.
@ -46,28 +47,28 @@ public class MockDefinitionTests {
public void classToMockMustNotBeNull() throws Exception { public void classToMockMustNotBeNull() throws Exception {
this.thrown.expect(IllegalArgumentException.class); this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("TypeToMock must not be null"); this.thrown.expectMessage("TypeToMock must not be null");
new MockDefinition(null, null, null, null, null, false, null); new MockDefinition(null, null, null, null, false, null, null);
} }
@Test @Test
public void createWithDefaults() throws Exception { public void createWithDefaults() throws Exception {
MockDefinition definition = new MockDefinition(null, null, EXAMPLE_SERVICE_TYPE, MockDefinition definition = new MockDefinition(null, EXAMPLE_SERVICE_TYPE, null,
null, null, false, null); null, false, null, null);
assertThat(definition.getElement()).isNull();
assertThat(definition.getName()).isNull(); assertThat(definition.getName()).isNull();
assertThat(definition.getTypeToMock()).isEqualTo(EXAMPLE_SERVICE_TYPE); assertThat(definition.getTypeToMock()).isEqualTo(EXAMPLE_SERVICE_TYPE);
assertThat(definition.getExtraInterfaces()).isEmpty(); assertThat(definition.getExtraInterfaces()).isEmpty();
assertThat(definition.getAnswer()).isEqualTo(Answers.RETURNS_DEFAULTS); assertThat(definition.getAnswer()).isEqualTo(Answers.RETURNS_DEFAULTS);
assertThat(definition.isSerializable()).isFalse(); assertThat(definition.isSerializable()).isFalse();
assertThat(definition.getReset()).isEqualTo(MockReset.AFTER); assertThat(definition.getReset()).isEqualTo(MockReset.AFTER);
assertThat(definition.getQualifier()).isNull();
} }
@Test @Test
public void createExplicit() throws Exception { public void createExplicit() throws Exception {
MockDefinition definition = new MockDefinition(getClass(), "name", QualifierDefinition qualifier = mock(QualifierDefinition.class);
EXAMPLE_SERVICE_TYPE, new Class<?>[] { ExampleExtraInterface.class }, MockDefinition definition = new MockDefinition("name", EXAMPLE_SERVICE_TYPE,
Answers.RETURNS_SMART_NULLS, true, MockReset.BEFORE); new Class<?>[] { ExampleExtraInterface.class },
assertThat(definition.getElement()).isEqualTo(getClass()); Answers.RETURNS_SMART_NULLS, true, MockReset.BEFORE, qualifier);
assertThat(definition.getName()).isEqualTo("name"); assertThat(definition.getName()).isEqualTo("name");
assertThat(definition.getTypeToMock()).isEqualTo(EXAMPLE_SERVICE_TYPE); assertThat(definition.getTypeToMock()).isEqualTo(EXAMPLE_SERVICE_TYPE);
assertThat(definition.getExtraInterfaces()) assertThat(definition.getExtraInterfaces())
@ -76,13 +77,14 @@ public class MockDefinitionTests {
assertThat(definition.isSerializable()).isTrue(); assertThat(definition.isSerializable()).isTrue();
assertThat(definition.getReset()).isEqualTo(MockReset.BEFORE); assertThat(definition.getReset()).isEqualTo(MockReset.BEFORE);
assertThat(definition.isProxyTargetAware()).isFalse(); assertThat(definition.isProxyTargetAware()).isFalse();
assertThat(definition.getQualifier()).isEqualTo(qualifier);
} }
@Test @Test
public void createMock() throws Exception { public void createMock() throws Exception {
MockDefinition definition = new MockDefinition(null, "name", EXAMPLE_SERVICE_TYPE, MockDefinition definition = new MockDefinition("name", EXAMPLE_SERVICE_TYPE,
new Class<?>[] { ExampleExtraInterface.class }, new Class<?>[] { ExampleExtraInterface.class },
Answers.RETURNS_SMART_NULLS, true, MockReset.BEFORE); Answers.RETURNS_SMART_NULLS, true, MockReset.BEFORE, null);
ExampleService mock = definition.createMock(); ExampleService mock = definition.createMock();
MockCreationSettings<?> settings = new MockUtil().getMockSettings(mock); MockCreationSettings<?> settings = new MockUtil().getMockSettings(mock);
assertThat(mock).isInstanceOf(ExampleService.class); assertThat(mock).isInstanceOf(ExampleService.class);

@ -53,8 +53,8 @@ public class MockitoContextCustomizerTests {
} }
private MockDefinition createTestMockDefinition(Class<?> typeToMock) { private MockDefinition createTestMockDefinition(Class<?> typeToMock) {
return new MockDefinition(null, null, ResolvableType.forClass(typeToMock), null, return new MockDefinition(null, ResolvableType.forClass(typeToMock), null, null,
null, false, null); false, null, null);
} }
} }

@ -0,0 +1,178 @@
/*
* Copyright 2012-2016 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
*
* http://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.boot.test.mock.mockito;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link QualifierDefinition}.
*
* @author Phillip Webb
*/
public class QualifierDefinitionTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Mock
private ConfigurableListableBeanFactory beanFactory;
@Captor
private ArgumentCaptor<DependencyDescriptor> descriptorCaptor;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void forElementFieldIsNullShouldReturnNull() throws Exception {
assertThat(QualifierDefinition.forElement((Field) null)).isNull();
}
@Test
public void forElementWhenElementIsNotFieldShouldReturnNull() throws Exception {
assertThat(QualifierDefinition.forElement(getClass())).isNull();
}
@Test
public void forElementWhenElementIsFieldWithNoQualifiersShouldReturnNull()
throws Exception {
QualifierDefinition definition = QualifierDefinition
.forElement(ReflectionUtils.findField(ConfigA.class, "noQualifier"));
assertThat(definition).isNull();
}
@Test
public void forElementWhenElementIsFieldWithQualifierShouldReturnDefinition()
throws Exception {
QualifierDefinition definition = QualifierDefinition
.forElement(ReflectionUtils.findField(ConfigA.class, "directQualifier"));
assertThat(definition).isNotNull();
}
@Test
public void matchesShouldCallBeanFactory() throws Exception {
Field field = ReflectionUtils.findField(ConfigA.class, "directQualifier");
QualifierDefinition qualifierDefinition = QualifierDefinition.forElement(field);
qualifierDefinition.matches(this.beanFactory, "bean");
verify(this.beanFactory).isAutowireCandidate(eq("bean"),
this.descriptorCaptor.capture());
assertThat(this.descriptorCaptor.getValue().getAnnotatedElement())
.isEqualTo(field);
}
@Test
public void applyToShouldSetQualifierElement() throws Exception {
Field field = ReflectionUtils.findField(ConfigA.class, "directQualifier");
QualifierDefinition qualifierDefinition = QualifierDefinition.forElement(field);
RootBeanDefinition definition = new RootBeanDefinition();
qualifierDefinition.applyTo(definition);
assertThat(definition.getQualifiedElement()).isEqualTo(field);
}
@Test
public void hashCodeAndEqualsShouldWorkOnDifferentClasses() throws Exception {
QualifierDefinition directQualifier1 = QualifierDefinition
.forElement(ReflectionUtils.findField(ConfigA.class, "directQualifier"));
QualifierDefinition directQualifier2 = QualifierDefinition
.forElement(ReflectionUtils.findField(ConfigB.class, "directQualifier"));
QualifierDefinition differentDirectQualifier1 = QualifierDefinition.forElement(
ReflectionUtils.findField(ConfigA.class, "differentDirectQualifier"));
QualifierDefinition differentDirectQualifier2 = QualifierDefinition.forElement(
ReflectionUtils.findField(ConfigB.class, "differentDirectQualifier"));
QualifierDefinition customQualifier1 = QualifierDefinition
.forElement(ReflectionUtils.findField(ConfigA.class, "customQualifier"));
QualifierDefinition customQualifier2 = QualifierDefinition
.forElement(ReflectionUtils.findField(ConfigB.class, "customQualifier"));
assertThat(directQualifier1.hashCode()).isEqualTo(directQualifier2.hashCode());
assertThat(differentDirectQualifier1.hashCode())
.isEqualTo(differentDirectQualifier2.hashCode());
assertThat(customQualifier1.hashCode()).isEqualTo(customQualifier2.hashCode());
assertThat(differentDirectQualifier1).isEqualTo(differentDirectQualifier1)
.isEqualTo(differentDirectQualifier2).isNotEqualTo(directQualifier2);
assertThat(directQualifier1).isEqualTo(directQualifier1)
.isEqualTo(directQualifier2).isNotEqualTo(differentDirectQualifier1);
assertThat(customQualifier1).isEqualTo(customQualifier1)
.isEqualTo(customQualifier2).isNotEqualTo(differentDirectQualifier1);
}
@Configuration
static class ConfigA {
@MockBean
private Object noQualifier;
@MockBean
@Qualifier("test")
private Object directQualifier;
@MockBean
@Qualifier("different")
private Object differentDirectQualifier;
@MockBean
@CustomQualifier
private Object customQualifier;
}
static class ConfigB {
@MockBean
@Qualifier("test")
private Object directQualifier;
@MockBean
@Qualifier("different")
private Object differentDirectQualifier;
@MockBean
@CustomQualifier
private Object customQualifier;
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomQualifier {
}
}

@ -29,6 +29,7 @@ import org.springframework.boot.test.mock.mockito.example.RealExampleService;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/** /**
* Tests for {@link SpyDefinition}. * Tests for {@link SpyDefinition}.
@ -47,35 +48,36 @@ public class SpyDefinitionTests {
public void classToSpyMustNotBeNull() throws Exception { public void classToSpyMustNotBeNull() throws Exception {
this.thrown.expect(IllegalArgumentException.class); this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("TypeToSpy must not be null"); this.thrown.expectMessage("TypeToSpy must not be null");
new SpyDefinition(null, null, null, null, true); new SpyDefinition(null, null, null, true, null);
} }
@Test @Test
public void createWithDefaults() throws Exception { public void createWithDefaults() throws Exception {
SpyDefinition definition = new SpyDefinition(null, null, REAL_SERVICE_TYPE, null, SpyDefinition definition = new SpyDefinition(null, REAL_SERVICE_TYPE, null, true,
true); null);
assertThat(definition.getElement()).isNull();
assertThat(definition.getName()).isNull(); assertThat(definition.getName()).isNull();
assertThat(definition.getTypeToSpy()).isEqualTo(REAL_SERVICE_TYPE); assertThat(definition.getTypeToSpy()).isEqualTo(REAL_SERVICE_TYPE);
assertThat(definition.getReset()).isEqualTo(MockReset.AFTER); assertThat(definition.getReset()).isEqualTo(MockReset.AFTER);
assertThat(definition.isProxyTargetAware()).isTrue(); assertThat(definition.isProxyTargetAware()).isTrue();
assertThat(definition.getQualifier()).isNull();
} }
@Test @Test
public void createExplicit() throws Exception { public void createExplicit() throws Exception {
SpyDefinition definition = new SpyDefinition(getClass(), "name", QualifierDefinition qualifier = mock(QualifierDefinition.class);
REAL_SERVICE_TYPE, MockReset.BEFORE, false); SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE,
assertThat(definition.getElement()).isEqualTo(getClass()); MockReset.BEFORE, false, qualifier);
assertThat(definition.getName()).isEqualTo("name"); assertThat(definition.getName()).isEqualTo("name");
assertThat(definition.getTypeToSpy()).isEqualTo(REAL_SERVICE_TYPE); assertThat(definition.getTypeToSpy()).isEqualTo(REAL_SERVICE_TYPE);
assertThat(definition.getReset()).isEqualTo(MockReset.BEFORE); assertThat(definition.getReset()).isEqualTo(MockReset.BEFORE);
assertThat(definition.isProxyTargetAware()).isFalse(); assertThat(definition.isProxyTargetAware()).isFalse();
assertThat(definition.getQualifier()).isEqualTo(qualifier);
} }
@Test @Test
public void createSpy() throws Exception { public void createSpy() throws Exception {
SpyDefinition definition = new SpyDefinition(null, "name", REAL_SERVICE_TYPE, SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE,
MockReset.BEFORE, true); MockReset.BEFORE, true, null);
RealExampleService spy = definition.createSpy(new RealExampleService("hello")); RealExampleService spy = definition.createSpy(new RealExampleService("hello"));
MockCreationSettings<?> settings = new MockUtil().getMockSettings(spy); MockCreationSettings<?> settings = new MockUtil().getMockSettings(spy);
assertThat(spy).isInstanceOf(ExampleService.class); assertThat(spy).isInstanceOf(ExampleService.class);
@ -87,8 +89,8 @@ public class SpyDefinitionTests {
@Test @Test
public void createSpyWhenNullInstanceShouldThrowException() throws Exception { public void createSpyWhenNullInstanceShouldThrowException() throws Exception {
SpyDefinition definition = new SpyDefinition(null, "name", REAL_SERVICE_TYPE, SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE,
MockReset.BEFORE, true); MockReset.BEFORE, true, null);
this.thrown.expect(IllegalArgumentException.class); this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Instance must not be null"); this.thrown.expectMessage("Instance must not be null");
definition.createSpy(null); definition.createSpy(null);
@ -96,8 +98,8 @@ public class SpyDefinitionTests {
@Test @Test
public void createSpyWhenWrongInstanceShouldThrowException() throws Exception { public void createSpyWhenWrongInstanceShouldThrowException() throws Exception {
SpyDefinition definition = new SpyDefinition(null, "name", REAL_SERVICE_TYPE, SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE,
MockReset.BEFORE, true); MockReset.BEFORE, true, null);
this.thrown.expect(IllegalArgumentException.class); this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("must be an instance of"); this.thrown.expectMessage("must be an instance of");
definition.createSpy(new ExampleServiceCaller(null)); definition.createSpy(new ExampleServiceCaller(null));
@ -105,8 +107,8 @@ public class SpyDefinitionTests {
@Test @Test
public void createSpyTwice() throws Exception { public void createSpyTwice() throws Exception {
SpyDefinition definition = new SpyDefinition(null, "name", REAL_SERVICE_TYPE, SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE,
MockReset.BEFORE, true); MockReset.BEFORE, true, null);
Object instance = new RealExampleService("hello"); Object instance = new RealExampleService("hello");
instance = definition.createSpy(instance); instance = definition.createSpy(instance);
instance = definition.createSpy(instance); instance = definition.createSpy(instance);

Loading…
Cancel
Save