diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/Definition.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/Definition.java index e1dbd0b18c..fff5f0b7f8 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/Definition.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/Definition.java @@ -16,6 +16,8 @@ package org.springframework.boot.test.mock.mockito; +import java.lang.reflect.AnnotatedElement; + import org.springframework.util.ObjectUtils; /** @@ -28,18 +30,29 @@ abstract class Definition { private static final int MULTIPLIER = 31; + private final AnnotatedElement element; + private final String name; private final MockReset reset; private final boolean proxyTargetAware; - Definition(String name, MockReset reset, boolean proxyTargetAware) { + Definition(AnnotatedElement element, String name, MockReset reset, boolean proxyTargetAware) { + this.element = element; this.name = name; this.reset = (reset != null ? reset : MockReset.AFTER); this.proxyTargetAware = proxyTargetAware; } + /** + * Return the {@link AnnotatedElement} that holds this definition. + * @return the element that defines this definition or {@code null} + */ + public AnnotatedElement getElement() { + return this.element; + } + /** * Return the name for bean. * @return the name or {@code null} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/DefinitionsParser.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/DefinitionsParser.java index 84407aa6dd..1b16fa2d1e 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/DefinitionsParser.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/DefinitionsParser.java @@ -38,6 +38,7 @@ import org.springframework.util.StringUtils; * class. * * @author Phillip Webb + * @author Stephane Nicoll */ class DefinitionsParser { @@ -90,10 +91,10 @@ class DefinitionsParser { "The name attribute can only be used when mocking a single class"); } for (ResolvableType typeToMock : typesToMock) { - MockDefinition definition = new MockDefinition(annotation.name(), typeToMock, - annotation.extraInterfaces(), annotation.answer(), + MockDefinition definition = new MockDefinition(element, annotation.name(), + typeToMock, annotation.extraInterfaces(), annotation.answer(), annotation.serializable(), annotation.reset()); - addDefinition(element, definition, "mock"); + addDefinition(definition, "mock"); } } @@ -106,16 +107,17 @@ class DefinitionsParser { "The name attribute can only be used when spying a single class"); } for (ResolvableType typeToSpy : typesToSpy) { - SpyDefinition definition = new SpyDefinition(annotation.name(), typeToSpy, - annotation.reset(), annotation.proxyTargetAware()); - addDefinition(element, definition, "spy"); + SpyDefinition definition = new SpyDefinition(element, annotation.name(), + typeToSpy, annotation.reset(), annotation.proxyTargetAware()); + addDefinition(definition, "spy"); } } - private void addDefinition(AnnotatedElement element, Definition definition, + private void addDefinition(Definition definition, String type) { boolean isNewDefinition = this.definitions.add(definition); Assert.state(isNewDefinition, "Duplicate " + type + " definition " + definition); + AnnotatedElement element = definition.getElement(); if (element instanceof Field) { Field field = (Field) element; this.definitionFields.put(definition, field); diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockBean.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockBean.java index 4f76b9f6df..76ae4c0bd2 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockBean.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockBean.java @@ -67,6 +67,18 @@ import org.springframework.test.context.junit4.SpringRunner; * * } * + * If there is more than one bean of the requested type, qualifier metadata must be + * specified at field level:
+ * @RunWith(SpringRunner.class)
+ * public class ExampleTests {
+ *
+ *     @MockBean
+ *     @Qualifier("example")
+ *     private ExampleService service;
+ *
+ *     ...
+ * }
+ * 
*

* This annotation is {@code @Repeatable} and may be specified multiple times when working * with Java 8 or contained within an {@link MockBeans @MockBeans} annotation. diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockDefinition.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockDefinition.java index 013eb67676..ae52ee62de 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockDefinition.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockDefinition.java @@ -16,6 +16,7 @@ package org.springframework.boot.test.mock.mockito; +import java.lang.reflect.AnnotatedElement; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; @@ -49,17 +50,9 @@ class MockDefinition extends Definition { private final boolean serializable; - MockDefinition(Class classToMock) { - this(ResolvableType.forClass(classToMock)); - } - - MockDefinition(ResolvableType typeToMock) { - this(null, typeToMock, null, null, false, null); - } - - MockDefinition(String name, ResolvableType typeToMock, Class[] extraInterfaces, + MockDefinition(AnnotatedElement element, String name, ResolvableType typeToMock, Class[] extraInterfaces, Answers answer, boolean serializable, MockReset reset) { - super(name, reset, false); + super(element, name, reset, false); Assert.notNull(typeToMock, "TypeToMock must not be null"); this.typeToMock = typeToMock; this.extraInterfaces = asClassSet(extraInterfaces); diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessor.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessor.java index d666c64441..a816896f92 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessor.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessor.java @@ -17,6 +17,7 @@ package org.springframework.boot.test.mock.mockito; import java.beans.PropertyDescriptor; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.util.Arrays; import java.util.HashMap; @@ -43,6 +44,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConstructorArgumentValues; 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.RuntimeBeanReference; import org.springframework.beans.factory.support.BeanDefinitionRegistry; @@ -72,6 +74,7 @@ import org.springframework.util.StringUtils; * * @author Phillip Webb * @author Andy Wilkinson + * @author Stephane Nicoll * @since 1.4.0 */ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAdapter @@ -206,6 +209,10 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda definition.setFactoryMethodName("createMock"); definition.getConstructorArgumentValues().addIndexedArgumentValue(0, mockDefinition); + AnnotatedElement element = mockDefinition.getElement(); + if (element instanceof Field) { + definition.setQualifiedElement(element); + } return definition; } @@ -225,8 +232,7 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda if (StringUtils.hasLength(mockDefinition.getName())) { return mockDefinition.getName(); } - String[] existingBeans = getExistingBeans(beanFactory, - mockDefinition.getTypeToMock()); + String[] existingBeans = findCandidateBeans(beanFactory, mockDefinition); if (ObjectUtils.isEmpty(existingBeans)) { return this.beanNameGenerator.generateBeanName(beanDefinition, registry); } @@ -235,7 +241,7 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda } throw new IllegalStateException( "Unable to register mock bean " + mockDefinition.getTypeToMock() - + " expected a single existing bean to replace but found " + + " expected a single matching bean to replace but found " + new TreeSet(Arrays.asList(existingBeans))); } @@ -250,6 +256,24 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda } } + private String[] findCandidateBeans(ConfigurableListableBeanFactory beanFactory, + MockDefinition mockDefinition) { + String[] beans = getExistingBeans(beanFactory, mockDefinition.getTypeToMock()); + // Attempt to filter using qualifiers + if (beans.length > 1 && mockDefinition.getElement() instanceof Field) { + DependencyDescriptor descriptor = new DependencyDescriptor( + (Field) mockDefinition.getElement(), true); + Set candidates = new LinkedHashSet(); + for (String bean : beans) { + if (beanFactory.isAutowireCandidate(bean, descriptor)) { + candidates.add(bean); + } + } + return candidates.toArray(new String[candidates.size()]); + } + return beans; + } + private String[] getExistingBeans(ConfigurableListableBeanFactory beanFactory, ResolvableType type) { Set beans = new LinkedHashSet( diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpyBean.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpyBean.java index f37c9e1dfc..980f37e4ef 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpyBean.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpyBean.java @@ -67,6 +67,18 @@ import org.springframework.test.context.junit4.SpringRunner; * * } * + * If there is more than one bean of the requested type, qualifier metadata must be + * specified at field level:

+ * @RunWith(SpringRunner.class)
+ * public class ExampleTests {
+ *
+ *     @SpyBean
+ *     @Qualifier("example")
+ *     private ExampleService service;
+ *
+ *     ...
+ * }
+ * 
*

* This annotation is {@code @Repeatable} and may be specified multiple times when working * with Java 8 or contained within a {@link SpyBeans @SpyBeans} annotation. diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpyDefinition.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpyDefinition.java index f040e5f14e..3c88a45b4d 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpyDefinition.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpyDefinition.java @@ -16,6 +16,8 @@ package org.springframework.boot.test.mock.mockito; +import java.lang.reflect.AnnotatedElement; + import org.mockito.MockSettings; import org.mockito.Mockito; import org.mockito.internal.util.MockUtil; @@ -39,9 +41,9 @@ class SpyDefinition extends Definition { private final ResolvableType typeToSpy; - SpyDefinition(String name, ResolvableType typeToSpy, MockReset reset, - boolean proxyTargetAware) { - super(name, reset, proxyTargetAware); + SpyDefinition(AnnotatedElement element, String name, ResolvableType typeToSpy, + MockReset reset, boolean proxyTargetAware) { + super(element, name, reset, proxyTargetAware); Assert.notNull(typeToSpy, "TypeToSpy must not be null"); this.typeToSpy = typeToSpy; diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/DefinitionsParserTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/DefinitionsParserTests.java index 5f5d2c54b6..bcd7b49176 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/DefinitionsParserTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/DefinitionsParserTests.java @@ -28,6 +28,7 @@ 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.ExampleServiceCaller; import org.springframework.boot.test.mock.mockito.example.RealExampleService; +import org.springframework.util.ReflectionUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -66,6 +67,7 @@ public class DefinitionsParserTests { this.parser.parse(MockBeanAttributes.class); assertThat(getDefinitions()).hasSize(1); MockDefinition definition = getMockDefinition(0); + assertThat(definition.getElement()).isEqualTo(MockBeanAttributes.class); assertThat(definition.getName()).isEqualTo("Name"); assertThat(definition.getTypeToMock().resolve()).isEqualTo(ExampleService.class); assertThat(definition.getExtraInterfaces()) @@ -79,9 +81,15 @@ public class DefinitionsParserTests { public void parseMockBeanOnClassAndField() throws Exception { this.parser.parse(MockBeanOnClassAndField.class); assertThat(getDefinitions()).hasSize(2); - assertThat(getMockDefinition(0).getTypeToMock().resolve()) + MockDefinition classDefinition = getMockDefinition(0); + assertThat(classDefinition.getElement()) + .isEqualTo(MockBeanOnClassAndField.class); + assertThat(classDefinition.getTypeToMock().resolve()) .isEqualTo(ExampleService.class); - assertThat(getMockDefinition(1).getTypeToMock().resolve()) + MockDefinition fieldDefinition = getMockDefinition(1); + assertThat(fieldDefinition.getElement()).isEqualTo( + ReflectionUtils.findField(MockBeanOnClassAndField.class, "caller")); + assertThat(fieldDefinition.getTypeToMock().resolve()) .isEqualTo(ExampleServiceCaller.class); } @@ -141,6 +149,7 @@ public class DefinitionsParserTests { this.parser.parse(SpyBeanAttributes.class); assertThat(getDefinitions()).hasSize(1); SpyDefinition definition = getSpyDefinition(0); + assertThat(definition.getElement()).isEqualTo(SpyBeanAttributes.class); assertThat(definition.getName()).isEqualTo("Name"); assertThat(definition.getTypeToSpy().resolve()) .isEqualTo(RealExampleService.class); @@ -151,10 +160,14 @@ public class DefinitionsParserTests { public void parseSpyBeanOnClassAndField() throws Exception { this.parser.parse(SpyBeanOnClassAndField.class); assertThat(getDefinitions()).hasSize(2); - assertThat(getSpyDefinition(0).getTypeToSpy().resolve()) + SpyDefinition classDefinition = getSpyDefinition(0); + assertThat(classDefinition.getElement()) + .isEqualTo(SpyBeanOnClassAndField.class); + assertThat(classDefinition.getTypeToSpy().resolve()) .isEqualTo(RealExampleService.class); - assertThat(getSpyDefinition(1).getTypeToSpy().resolve()) - .isEqualTo(ExampleServiceCaller.class); + SpyDefinition fieldDefinition = getSpyDefinition(1); + assertThat(fieldDefinition.getElement()).isEqualTo( + ReflectionUtils.findField(SpyBeanOnClassAndField.class, "caller")); } @Test diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestFieldForExistingBeanWithQualifierIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestFieldForExistingBeanWithQualifierIntegrationTests.java new file mode 100644 index 0000000000..a87f89d9be --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestFieldForExistingBeanWithQualifierIntegrationTests.java @@ -0,0 +1,87 @@ +/* + * 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 org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.example.CustomQualifier; +import org.springframework.boot.test.mock.mockito.example.CustomQualifierExampleService; +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.RealExampleService; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.verify; + +/** + * Test {@link MockBean} on a test class field can be used to replace existing bean + * while preserving qualifiers. + */ +@RunWith(SpringRunner.class) +public class MockBeanOnTestFieldForExistingBeanWithQualifierIntegrationTests { + + @MockBean + @CustomQualifier + private ExampleService service; + + @Autowired + private ExampleServiceCaller caller; + + @Autowired + private ApplicationContext applicationContext; + + @Test + public void testMocking() throws Exception { + this.caller.sayGreeting(); + verify(this.service).greeting(); + } + + @Test + public void onlyQualifiedBeanIsReplaced() { + assertThat(this.applicationContext.getBean("service")).isSameAs(this.service); + ExampleService anotherService = this.applicationContext.getBean( + "anotherService", ExampleService.class); + assertThat(anotherService.greeting()).isEqualTo("Another"); + } + + @Configuration + static class TestConfig { + + @Bean + public CustomQualifierExampleService service() { + return new CustomQualifierExampleService(); + } + + @Bean + public ExampleService anotherService() { + return new RealExampleService("Another"); + } + + @Bean + public ExampleServiceCaller controller(@CustomQualifier ExampleService service) { + return new ExampleServiceCaller(service); + } + + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockDefinitionTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockDefinitionTests.java index 391381d5e3..e818178e59 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockDefinitionTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockDefinitionTests.java @@ -46,13 +46,14 @@ public class MockDefinitionTests { public void classToMockMustNotBeNull() throws Exception { this.thrown.expect(IllegalArgumentException.class); this.thrown.expectMessage("TypeToMock must not be null"); - new MockDefinition(null, null, null, null, false, null); + new MockDefinition(null, null, null, null, null, false, null); } @Test public void createWithDefaults() throws Exception { - MockDefinition definition = new MockDefinition(null, EXAMPLE_SERVICE_TYPE, null, - null, false, null); + MockDefinition definition = new MockDefinition(null, null, EXAMPLE_SERVICE_TYPE, + null, null, false, null); + assertThat(definition.getElement()).isNull(); assertThat(definition.getName()).isNull(); assertThat(definition.getTypeToMock()).isEqualTo(EXAMPLE_SERVICE_TYPE); assertThat(definition.getExtraInterfaces()).isEmpty(); @@ -63,9 +64,11 @@ public class MockDefinitionTests { @Test public void createExplicit() throws Exception { - MockDefinition definition = new MockDefinition("name", EXAMPLE_SERVICE_TYPE, + MockDefinition definition = new MockDefinition(getClass(), "name", + EXAMPLE_SERVICE_TYPE, new Class[] { ExampleExtraInterface.class }, Answers.RETURNS_SMART_NULLS, true, MockReset.BEFORE); + assertThat(definition.getElement()).isEqualTo(getClass()); assertThat(definition.getName()).isEqualTo("name"); assertThat(definition.getTypeToMock()).isEqualTo(EXAMPLE_SERVICE_TYPE); assertThat(definition.getExtraInterfaces()) @@ -78,7 +81,8 @@ public class MockDefinitionTests { @Test public void createMock() throws Exception { - MockDefinition definition = new MockDefinition("name", EXAMPLE_SERVICE_TYPE, + MockDefinition definition = new MockDefinition(null, "name", + EXAMPLE_SERVICE_TYPE, new Class[] { ExampleExtraInterface.class }, Answers.RETURNS_SMART_NULLS, true, MockReset.BEFORE); ExampleService mock = definition.createMock(); diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizerTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizerTests.java index 4c2b0a4b71..f8e1a8c1ce 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizerTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizerTests.java @@ -25,6 +25,7 @@ import org.junit.Test; import org.springframework.boot.test.mock.mockito.example.ExampleService; import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller; +import org.springframework.core.ResolvableType; import static org.assertj.core.api.Assertions.assertThat; @@ -39,8 +40,8 @@ public class MockitoContextCustomizerTests { @Test public void hashCodeAndEquals() { - MockDefinition d1 = new MockDefinition(ExampleService.class); - MockDefinition d2 = new MockDefinition(ExampleServiceCaller.class); + MockDefinition d1 = createTestMockDefinition(ExampleService.class); + MockDefinition d2 = createTestMockDefinition(ExampleServiceCaller.class); MockitoContextCustomizer c1 = new MockitoContextCustomizer(NO_DEFINITIONS); MockitoContextCustomizer c2 = new MockitoContextCustomizer( new LinkedHashSet(Arrays.asList(d1, d2))); @@ -51,4 +52,8 @@ public class MockitoContextCustomizerTests { assertThat(c2).isEqualTo(c2).isEqualTo(c3).isNotEqualTo(c1); } + private MockDefinition createTestMockDefinition(Class typeToMock) { + return new MockDefinition(null, null, ResolvableType.forClass(typeToMock), null, null, false, null); + } + } diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessorTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessorTests.java index a88c6b9824..8bfc6d9b1f 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessorTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessorTests.java @@ -22,6 +22,7 @@ import org.junit.rules.ExpectedException; import org.mockito.internal.util.MockUtil; import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.test.mock.mockito.example.ExampleService; import org.springframework.boot.test.mock.mockito.example.FailingExampleService; @@ -50,11 +51,24 @@ public class MockitoPostProcessorTests { this.thrown.expect(IllegalStateException.class); this.thrown.expectMessage( "Unable to register mock bean " + ExampleService.class.getName() - + " expected a single existing bean to replace " + + " expected a single matching bean to replace " + "but found [example1, example2]"); context.refresh(); } + @Test + public void cannotMockMultipleQualifiedBeans() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + MockitoPostProcessor.register(context); + context.register(MultipleQualifiedBeans.class); + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage( + "Unable to register mock bean " + ExampleService.class.getName() + + " expected a single matching bean to replace " + + "but found [example1, example3]"); + context.refresh(); + } + @Test public void canMockBeanProducedByFactoryBeanWithObjectTypeAttribute() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); @@ -96,6 +110,32 @@ public class MockitoPostProcessorTests { } + @Configuration + static class MultipleQualifiedBeans { + + @MockBean(ExampleService.class) + @Qualifier("test") + private ExampleService mock; + + @Bean + @Qualifier("test") + public ExampleService example1() { + return new FailingExampleService(); + } + + @Bean + public ExampleService example2() { + return new FailingExampleService(); + } + + @Bean + @Qualifier("test") + public ExampleService example3() { + return new FailingExampleService(); + } + + } + static class TestFactoryBean implements FactoryBean { @Override diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyDefinitionTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyDefinitionTests.java index fb738f52ac..91399c7adb 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyDefinitionTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyDefinitionTests.java @@ -47,12 +47,13 @@ public class SpyDefinitionTests { public void classToSpyMustNotBeNull() throws Exception { this.thrown.expect(IllegalArgumentException.class); this.thrown.expectMessage("TypeToSpy must not be null"); - new SpyDefinition(null, null, null, true); + new SpyDefinition(null, null, null, null, true); } @Test public void createWithDefaults() throws Exception { - SpyDefinition definition = new SpyDefinition(null, REAL_SERVICE_TYPE, null, true); + SpyDefinition definition = new SpyDefinition(null, null, REAL_SERVICE_TYPE, null, true); + assertThat(definition.getElement()).isNull(); assertThat(definition.getName()).isNull(); assertThat(definition.getTypeToSpy()).isEqualTo(REAL_SERVICE_TYPE); assertThat(definition.getReset()).isEqualTo(MockReset.AFTER); @@ -61,8 +62,9 @@ public class SpyDefinitionTests { @Test public void createExplicit() throws Exception { - SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE, - MockReset.BEFORE, false); + SpyDefinition definition = new SpyDefinition(getClass(), "name", + REAL_SERVICE_TYPE, MockReset.BEFORE, false); + assertThat(definition.getElement()).isEqualTo(getClass()); assertThat(definition.getName()).isEqualTo("name"); assertThat(definition.getTypeToSpy()).isEqualTo(REAL_SERVICE_TYPE); assertThat(definition.getReset()).isEqualTo(MockReset.BEFORE); @@ -71,7 +73,7 @@ public class SpyDefinitionTests { @Test public void createSpy() throws Exception { - SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE, + SpyDefinition definition = new SpyDefinition(null, "name", REAL_SERVICE_TYPE, MockReset.BEFORE, true); RealExampleService spy = definition.createSpy(new RealExampleService("hello")); MockCreationSettings settings = new MockUtil().getMockSettings(spy); @@ -84,7 +86,7 @@ public class SpyDefinitionTests { @Test public void createSpyWhenNullInstanceShouldThrowException() throws Exception { - SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE, + SpyDefinition definition = new SpyDefinition(null, "name", REAL_SERVICE_TYPE, MockReset.BEFORE, true); this.thrown.expect(IllegalArgumentException.class); this.thrown.expectMessage("Instance must not be null"); @@ -93,7 +95,7 @@ public class SpyDefinitionTests { @Test public void createSpyWhenWrongInstanceShouldThrowException() throws Exception { - SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE, + SpyDefinition definition = new SpyDefinition(null, "name", REAL_SERVICE_TYPE, MockReset.BEFORE, true); this.thrown.expect(IllegalArgumentException.class); this.thrown.expectMessage("must be an instance of"); @@ -102,7 +104,7 @@ public class SpyDefinitionTests { @Test public void createSpyTwice() throws Exception { - SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE, + SpyDefinition definition = new SpyDefinition(null, "name", REAL_SERVICE_TYPE, MockReset.BEFORE, true); Object instance = new RealExampleService("hello"); instance = definition.createSpy(instance); diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/CustomQualifier.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/CustomQualifier.java new file mode 100644 index 0000000000..89fbd166cd --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/CustomQualifier.java @@ -0,0 +1,28 @@ +/* + * 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.example; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.springframework.beans.factory.annotation.Qualifier; + +@Qualifier +@Retention(RetentionPolicy.RUNTIME) +public @interface CustomQualifier { + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/CustomQualifierExampleService.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/CustomQualifierExampleService.java new file mode 100644 index 0000000000..44d29c5218 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/CustomQualifierExampleService.java @@ -0,0 +1,32 @@ +/* + * 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.example; + +/** + * An {@link ExampleService} that uses a custom qualifier. + * + * @author Andy Wilkinson + */ +@CustomQualifier +public class CustomQualifierExampleService implements ExampleService { + + @Override + public String greeting() { + return "CustomQualifier"; + } + +}