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 07249d0210..227842b174 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 @@ -18,7 +18,6 @@ package org.springframework.boot.test.mock.mockito; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; @@ -26,6 +25,7 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; +import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; @@ -82,15 +82,15 @@ class DefinitionsParser { } private void parseMockBeanAnnotation(MockBean annotation, AnnotatedElement element) { - Set> classesToMock = getOrDeduceClasses(element, annotation.value()); - Assert.state(!classesToMock.isEmpty(), - "Unable to deduce class to mock from " + element); + Set typesToMock = getOrDeduceTypes(element, annotation.value()); + Assert.state(!typesToMock.isEmpty(), + "Unable to deduce type to mock from " + element); if (StringUtils.hasLength(annotation.name())) { - Assert.state(classesToMock.size() == 1, + Assert.state(typesToMock.size() == 1, "The name attribute can only be used when mocking a single class"); } - for (Class classToMock : classesToMock) { - MockDefinition definition = new MockDefinition(annotation.name(), classToMock, + for (ResolvableType typeToMock : typesToMock) { + MockDefinition definition = new MockDefinition(annotation.name(), typeToMock, annotation.extraInterfaces(), annotation.answer(), annotation.serializable(), annotation.reset(), annotation.proxyTargetAware()); @@ -99,15 +99,15 @@ class DefinitionsParser { } private void parseSpyBeanAnnotation(SpyBean annotation, AnnotatedElement element) { - Set> classesToSpy = getOrDeduceClasses(element, annotation.value()); - Assert.state(!classesToSpy.isEmpty(), - "Unable to deduce class to spy from " + element); + Set typesToSpy = getOrDeduceTypes(element, annotation.value()); + Assert.state(!typesToSpy.isEmpty(), + "Unable to deduce type to spy from " + element); if (StringUtils.hasLength(annotation.name())) { - Assert.state(classesToSpy.size() == 1, + Assert.state(typesToSpy.size() == 1, "The name attribute can only be used when spying a single class"); } - for (Class classToSpy : classesToSpy) { - SpyDefinition definition = new SpyDefinition(annotation.name(), classToSpy, + for (ResolvableType typeToSpy : typesToSpy) { + SpyDefinition definition = new SpyDefinition(annotation.name(), typeToSpy, annotation.reset(), annotation.proxyTargetAware()); addDefinition(element, definition, "spy"); } @@ -123,13 +123,16 @@ class DefinitionsParser { } } - private Set> getOrDeduceClasses(AnnotatedElement element, Class[] value) { - Set> classes = new LinkedHashSet>(); - classes.addAll(Arrays.asList(value)); - if (classes.isEmpty() && element instanceof Field) { - classes.add(((Field) element).getType()); + private Set getOrDeduceTypes(AnnotatedElement element, + Class[] value) { + Set types = new LinkedHashSet(); + for (Class type : value) { + types.add(ResolvableType.forClass(type)); } - return classes; + if (types.isEmpty() && element instanceof Field) { + types.add(ResolvableType.forField((Field) element)); + } + return types; } public Set getDefinitions() { 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 75f00f2d00..5b8e49f45b 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 @@ -26,6 +26,7 @@ import org.mockito.MockSettings; import org.mockito.Mockito; import org.mockito.stubbing.Answer; +import org.springframework.core.ResolvableType; import org.springframework.core.style.ToStringCreator; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -40,7 +41,7 @@ class MockDefinition extends Definition { private static final int MULTIPLIER = 31; - private final Class classToMock; + private final ResolvableType typeToMock; private final Set> extraInterfaces; @@ -49,15 +50,19 @@ class MockDefinition extends Definition { private final boolean serializable; MockDefinition(Class classToMock) { - this(null, classToMock, null, null, false, null, true); + this(ResolvableType.forClass(classToMock)); } - MockDefinition(String name, Class classToMock, Class[] extraInterfaces, + MockDefinition(ResolvableType typeToMock) { + this(null, typeToMock, null, null, false, null, true); + } + + MockDefinition(String name, ResolvableType typeToMock, Class[] extraInterfaces, Answers answer, boolean serializable, MockReset reset, boolean proxyTargetAware) { super(name, reset, proxyTargetAware); - Assert.notNull(classToMock, "ClassToMock must not be null"); - this.classToMock = classToMock; + Assert.notNull(typeToMock, "TypeToMock must not be null"); + this.typeToMock = typeToMock; this.extraInterfaces = asClassSet(extraInterfaces); this.answer = (answer != null ? answer : Answers.RETURNS_DEFAULTS); this.serializable = serializable; @@ -72,11 +77,11 @@ class MockDefinition extends Definition { } /** - * Return the class that should be mocked. + * Return the type that should be mocked. * @return the class to mock; never {@code null} */ - public Class getClassToMock() { - return this.classToMock; + public ResolvableType getTypeToMock() { + return this.typeToMock; } /** @@ -106,7 +111,7 @@ class MockDefinition extends Definition { @Override public int hashCode() { int result = super.hashCode(); - result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.classToMock); + result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.typeToMock); result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.extraInterfaces); result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.answer); result = MULTIPLIER * result + (this.serializable ? 1231 : 1237); @@ -123,7 +128,7 @@ class MockDefinition extends Definition { } MockDefinition other = (MockDefinition) obj; boolean result = super.equals(obj); - result &= ObjectUtils.nullSafeEquals(this.classToMock, other.classToMock); + result &= ObjectUtils.nullSafeEquals(this.typeToMock, other.typeToMock); result &= ObjectUtils.nullSafeEquals(this.extraInterfaces, other.extraInterfaces); result &= ObjectUtils.nullSafeEquals(this.answer, other.answer); result &= this.serializable == other.serializable; @@ -133,7 +138,7 @@ class MockDefinition extends Definition { @Override public String toString() { return new ToStringCreator(this).append("name", getName()) - .append("classToMock", this.classToMock) + .append("typeToMock", this.typeToMock) .append("extraInterfaces", this.extraInterfaces) .append("answer", this.answer).append("serializable", this.serializable) .append("reset", getReset()).toString(); @@ -156,7 +161,7 @@ class MockDefinition extends Definition { if (this.serializable) { settings.serializable(); } - return (T) Mockito.mock(this.classToMock, settings); + return (T) Mockito.mock(this.typeToMock.resolve(), settings); } private Answer getAnswer(Answers answer) { 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 be9ddfff99..d0d91b9312 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 @@ -54,6 +54,7 @@ import org.springframework.context.annotation.ConfigurationClassPostProcessor; import org.springframework.core.Conventions; import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; +import org.springframework.core.ResolvableType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -190,8 +191,8 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda private RootBeanDefinition createBeanDefinition(MockDefinition mockDefinition) { RootBeanDefinition definition = new RootBeanDefinition( - mockDefinition.getClassToMock()); - definition.setTargetType(mockDefinition.getClassToMock()); + mockDefinition.getTypeToMock().resolve()); + definition.setTargetType(mockDefinition.getTypeToMock()); definition.setFactoryBeanName(BEAN_NAME); definition.setFactoryMethodName("createMock"); definition.getConstructorArgumentValues().addIndexedArgumentValue(0, @@ -216,23 +217,22 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda return mockDefinition.getName(); } String[] existingBeans = getExistingBeans(beanFactory, - mockDefinition.getClassToMock()); + mockDefinition.getTypeToMock()); if (ObjectUtils.isEmpty(existingBeans)) { return this.beanNameGenerator.generateBeanName(beanDefinition, registry); } if (existingBeans.length == 1) { return existingBeans[0]; } - throw new IllegalStateException("Unable to register mock bean " - + mockDefinition.getClassToMock().getName() - + " expected a single existing bean to replace but found " - + new TreeSet(Arrays.asList(existingBeans))); + throw new IllegalStateException( + "Unable to register mock bean " + mockDefinition.getTypeToMock() + + " expected a single existing bean to replace but found " + + new TreeSet(Arrays.asList(existingBeans))); } private void registerSpy(ConfigurableListableBeanFactory beanFactory, BeanDefinitionRegistry registry, SpyDefinition definition, Field field) { - String[] existingBeans = getExistingBeans(beanFactory, - definition.getClassToSpy()); + String[] existingBeans = getExistingBeans(beanFactory, definition.getTypeToSpy()); if (ObjectUtils.isEmpty(existingBeans)) { createSpy(registry, definition, field); } @@ -242,13 +242,14 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda } private String[] getExistingBeans(ConfigurableListableBeanFactory beanFactory, - Class type) { + ResolvableType type) { Set beans = new LinkedHashSet( Arrays.asList(beanFactory.getBeanNamesForType(type))); + String resolvedTypeName = type.resolve(Object.class).getName(); for (String beanName : beanFactory.getBeanNamesForType(FactoryBean.class)) { beanName = BeanFactoryUtils.transformedBeanName(beanName); BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); - if (type.getName() + if (resolvedTypeName .equals(beanDefinition.getAttribute(FACTORY_BEAN_OBJECT_TYPE))) { beans.add(beanName); } @@ -273,7 +274,7 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda private void createSpy(BeanDefinitionRegistry registry, SpyDefinition definition, Field field) { RootBeanDefinition beanDefinition = new RootBeanDefinition( - definition.getClassToSpy()); + definition.getTypeToSpy().resolve()); String beanName = this.beanNameGenerator.generateBeanName(beanDefinition, registry); registry.registerBeanDefinition(beanName, beanDefinition); @@ -283,7 +284,7 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda private void registerSpies(SpyDefinition definition, Field field, String[] existingBeans) { Assert.state(field == null || existingBeans.length == 1, - "Unable to register spy bean " + definition.getClassToSpy().getName() + "Unable to register spy bean " + definition.getTypeToSpy() + " expected a single existing bean to replace but found " + new TreeSet(Arrays.asList(existingBeans))); for (String beanName : existingBeans) { 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 94f483fb76..f37c9e1dfc 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 @@ -100,8 +100,8 @@ public @interface SpyBean { * The classes to spy. Each class specified here will result in a spy being applied. * Classes can be omitted when the annotation is used on a field. *

- * When {@code @SpyBean} also defines a {@code name} this attribute can only contain - * a single value. + * When {@code @SpyBean} also defines a {@code name} this attribute can only contain a + * single value. *

* If this is the only specified attribute consider using the {@code value} alias * instead. 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 46f69b0a61..f040e5f14e 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 @@ -20,6 +20,7 @@ import org.mockito.MockSettings; import org.mockito.Mockito; import org.mockito.internal.util.MockUtil; +import org.springframework.core.ResolvableType; import org.springframework.core.style.ToStringCreator; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -36,24 +37,24 @@ class SpyDefinition extends Definition { private static final int MULTIPLIER = 31; - private final Class classToSpy; + private final ResolvableType typeToSpy; - SpyDefinition(String name, Class classToSpy, MockReset reset, + SpyDefinition(String name, ResolvableType typeToSpy, MockReset reset, boolean proxyTargetAware) { super(name, reset, proxyTargetAware); - Assert.notNull(classToSpy, "ClassToSpy must not be null"); - this.classToSpy = classToSpy; + Assert.notNull(typeToSpy, "TypeToSpy must not be null"); + this.typeToSpy = typeToSpy; } - public Class getClassToSpy() { - return this.classToSpy; + public ResolvableType getTypeToSpy() { + return this.typeToSpy; } @Override public int hashCode() { int result = super.hashCode(); - result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.classToSpy); + result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.typeToSpy); return result; } @@ -67,14 +68,14 @@ class SpyDefinition extends Definition { } SpyDefinition other = (SpyDefinition) obj; boolean result = super.equals(obj); - result &= ObjectUtils.nullSafeEquals(this.classToSpy, other.classToSpy); + result &= ObjectUtils.nullSafeEquals(this.typeToSpy, other.typeToSpy); return result; } @Override public String toString() { return new ToStringCreator(this).append("name", getName()) - .append("classToSpy", this.classToSpy).append("reset", getReset()) + .append("typeToSpy", this.typeToSpy).append("reset", getReset()) .toString(); } @@ -85,7 +86,7 @@ class SpyDefinition extends Definition { @SuppressWarnings("unchecked") public T createSpy(String name, Object instance) { Assert.notNull(instance, "Instance must not be null"); - Assert.isInstanceOf(this.classToSpy, instance); + Assert.isInstanceOf(this.typeToSpy.resolve(), instance); if (this.mockUtil.isSpy(instance)) { return (T) instance; } 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 98002d1165..5f5d2c54b6 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 @@ -47,15 +47,17 @@ public class DefinitionsParserTests { public void parseSingleMockBean() { this.parser.parse(SingleMockBean.class); assertThat(getDefinitions()).hasSize(1); - assertThat(getMockDefinition(0).getClassToMock()).isEqualTo(ExampleService.class); + assertThat(getMockDefinition(0).getTypeToMock().resolve()) + .isEqualTo(ExampleService.class); } @Test public void parseRepeatMockBean() { this.parser.parse(RepeatMockBean.class); assertThat(getDefinitions()).hasSize(2); - assertThat(getMockDefinition(0).getClassToMock()).isEqualTo(ExampleService.class); - assertThat(getMockDefinition(1).getClassToMock()) + assertThat(getMockDefinition(0).getTypeToMock().resolve()) + .isEqualTo(ExampleService.class); + assertThat(getMockDefinition(1).getTypeToMock().resolve()) .isEqualTo(ExampleServiceCaller.class); } @@ -65,7 +67,7 @@ public class DefinitionsParserTests { assertThat(getDefinitions()).hasSize(1); MockDefinition definition = getMockDefinition(0); assertThat(definition.getName()).isEqualTo("Name"); - assertThat(definition.getClassToMock()).isEqualTo(ExampleService.class); + assertThat(definition.getTypeToMock().resolve()).isEqualTo(ExampleService.class); assertThat(definition.getExtraInterfaces()) .containsExactly(ExampleExtraInterface.class); assertThat(definition.getAnswer()).isEqualTo(Answers.RETURNS_SMART_NULLS); @@ -77,8 +79,9 @@ public class DefinitionsParserTests { public void parseMockBeanOnClassAndField() throws Exception { this.parser.parse(MockBeanOnClassAndField.class); assertThat(getDefinitions()).hasSize(2); - assertThat(getMockDefinition(0).getClassToMock()).isEqualTo(ExampleService.class); - assertThat(getMockDefinition(1).getClassToMock()) + assertThat(getMockDefinition(0).getTypeToMock().resolve()) + .isEqualTo(ExampleService.class); + assertThat(getMockDefinition(1).getTypeToMock().resolve()) .isEqualTo(ExampleServiceCaller.class); } @@ -86,13 +89,14 @@ public class DefinitionsParserTests { public void parseMockBeanInferClassToMock() throws Exception { this.parser.parse(MockBeanInferClassToMock.class); assertThat(getDefinitions()).hasSize(1); - assertThat(getMockDefinition(0).getClassToMock()).isEqualTo(ExampleService.class); + assertThat(getMockDefinition(0).getTypeToMock().resolve()) + .isEqualTo(ExampleService.class); } @Test public void parseMockBeanMissingClassToMock() throws Exception { this.thrown.expect(IllegalStateException.class); - this.thrown.expectMessage("Unable to deduce class to mock"); + this.thrown.expectMessage("Unable to deduce type to mock"); this.parser.parse(MockBeanMissingClassToMock.class); } @@ -100,8 +104,9 @@ public class DefinitionsParserTests { public void parseMockBeanMultipleClasses() throws Exception { this.parser.parse(MockBeanMultipleClasses.class); assertThat(getDefinitions()).hasSize(2); - assertThat(getMockDefinition(0).getClassToMock()).isEqualTo(ExampleService.class); - assertThat(getMockDefinition(1).getClassToMock()) + assertThat(getMockDefinition(0).getTypeToMock().resolve()) + .isEqualTo(ExampleService.class); + assertThat(getMockDefinition(1).getTypeToMock().resolve()) .isEqualTo(ExampleServiceCaller.class); } @@ -117,7 +122,7 @@ public class DefinitionsParserTests { public void parseSingleSpyBean() { this.parser.parse(SingleSpyBean.class); assertThat(getDefinitions()).hasSize(1); - assertThat(getSpyDefinition(0).getClassToSpy()) + assertThat(getSpyDefinition(0).getTypeToSpy().resolve()) .isEqualTo(RealExampleService.class); } @@ -125,9 +130,9 @@ public class DefinitionsParserTests { public void parseRepeatSpyBean() { this.parser.parse(RepeatSpyBean.class); assertThat(getDefinitions()).hasSize(2); - assertThat(getSpyDefinition(0).getClassToSpy()) + assertThat(getSpyDefinition(0).getTypeToSpy().resolve()) .isEqualTo(RealExampleService.class); - assertThat(getSpyDefinition(1).getClassToSpy()) + assertThat(getSpyDefinition(1).getTypeToSpy().resolve()) .isEqualTo(ExampleServiceCaller.class); } @@ -137,7 +142,8 @@ public class DefinitionsParserTests { assertThat(getDefinitions()).hasSize(1); SpyDefinition definition = getSpyDefinition(0); assertThat(definition.getName()).isEqualTo("Name"); - assertThat(definition.getClassToSpy()).isEqualTo(RealExampleService.class); + assertThat(definition.getTypeToSpy().resolve()) + .isEqualTo(RealExampleService.class); assertThat(definition.getReset()).isEqualTo(MockReset.NONE); } @@ -145,9 +151,9 @@ public class DefinitionsParserTests { public void parseSpyBeanOnClassAndField() throws Exception { this.parser.parse(SpyBeanOnClassAndField.class); assertThat(getDefinitions()).hasSize(2); - assertThat(getSpyDefinition(0).getClassToSpy()) + assertThat(getSpyDefinition(0).getTypeToSpy().resolve()) .isEqualTo(RealExampleService.class); - assertThat(getSpyDefinition(1).getClassToSpy()) + assertThat(getSpyDefinition(1).getTypeToSpy().resolve()) .isEqualTo(ExampleServiceCaller.class); } @@ -155,14 +161,14 @@ public class DefinitionsParserTests { public void parseSpyBeanInferClassToMock() throws Exception { this.parser.parse(SpyBeanInferClassToMock.class); assertThat(getDefinitions()).hasSize(1); - assertThat(getSpyDefinition(0).getClassToSpy()) + assertThat(getSpyDefinition(0).getTypeToSpy().resolve()) .isEqualTo(RealExampleService.class); } @Test public void parseSpyBeanMissingClassToMock() throws Exception { this.thrown.expect(IllegalStateException.class); - this.thrown.expectMessage("Unable to deduce class to spy"); + this.thrown.expectMessage("Unable to deduce type to spy"); this.parser.parse(SpyBeanMissingClassToMock.class); } @@ -170,9 +176,9 @@ public class DefinitionsParserTests { public void parseSpyBeanMultipleClasses() throws Exception { this.parser.parse(SpyBeanMultipleClasses.class); assertThat(getDefinitions()).hasSize(2); - assertThat(getSpyDefinition(0).getClassToSpy()) + assertThat(getSpyDefinition(0).getTypeToSpy().resolve()) .isEqualTo(RealExampleService.class); - assertThat(getSpyDefinition(1).getClassToSpy()) + assertThat(getSpyDefinition(1).getTypeToSpy().resolve()) .isEqualTo(ExampleServiceCaller.class); } diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanWithGenericsOnTestFieldForNewBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanWithGenericsOnTestFieldForNewBeanIntegrationTests.java new file mode 100644 index 0000000000..75ebe7c7e4 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanWithGenericsOnTestFieldForNewBeanIntegrationTests.java @@ -0,0 +1,62 @@ +/* + * 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.ExampleGenericService; +import org.springframework.boot.test.mock.mockito.example.ExampleGenericServiceCaller; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; + +/** + * Test {@link MockBean} on a test class field can be used to inject new mock instances. + * + * @author Phillip Webb + */ +@RunWith(SpringRunner.class) +public class MockBeanWithGenericsOnTestFieldForNewBeanIntegrationTests { + + @MockBean + private ExampleGenericService exampleIntegerService; + + @MockBean + private ExampleGenericService exampleStringService; + + @Autowired + private ExampleGenericServiceCaller caller; + + @Test + public void testMocking() throws Exception { + given(this.exampleIntegerService.greeting()).willReturn(200); + given(this.exampleStringService.greeting()).willReturn("Boot"); + assertThat(this.caller.sayGreeting()).isEqualTo("I say 200 Boot"); + } + + @Configuration + @Import(ExampleGenericServiceCaller.class) + static class Config { + + } + +} 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 950bb4fa92..72b91fab4a 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 @@ -25,6 +25,7 @@ import org.mockito.mock.MockCreationSettings; import org.springframework.boot.test.mock.mockito.example.ExampleExtraInterface; import org.springframework.boot.test.mock.mockito.example.ExampleService; +import org.springframework.core.ResolvableType; import static org.assertj.core.api.Assertions.assertThat; @@ -35,22 +36,25 @@ import static org.assertj.core.api.Assertions.assertThat; */ public class MockDefinitionTests { + private static final ResolvableType EXAMPLE_SERVICE_TYPE = ResolvableType + .forClass(ExampleService.class); + @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void ClassToMockMustNotBeNull() throws Exception { this.thrown.expect(IllegalArgumentException.class); - this.thrown.expectMessage("ClassToMock must not be null"); + this.thrown.expectMessage("TypeToMock must not be null"); new MockDefinition(null, null, null, null, false, null, true); } @Test public void createWithDefaults() throws Exception { - MockDefinition definition = new MockDefinition(null, ExampleService.class, null, + MockDefinition definition = new MockDefinition(null, EXAMPLE_SERVICE_TYPE, null, null, false, null, true); assertThat(definition.getName()).isNull(); - assertThat(definition.getClassToMock()).isEqualTo(ExampleService.class); + assertThat(definition.getTypeToMock()).isEqualTo(EXAMPLE_SERVICE_TYPE); assertThat(definition.getExtraInterfaces()).isEmpty(); assertThat(definition.getAnswer()).isEqualTo(Answers.RETURNS_DEFAULTS); assertThat(definition.isSerializable()).isFalse(); @@ -59,11 +63,11 @@ public class MockDefinitionTests { @Test public void createExplicit() throws Exception { - MockDefinition definition = new MockDefinition("name", ExampleService.class, + MockDefinition definition = new MockDefinition("name", EXAMPLE_SERVICE_TYPE, new Class[] { ExampleExtraInterface.class }, Answers.RETURNS_SMART_NULLS, true, MockReset.BEFORE, false); assertThat(definition.getName()).isEqualTo("name"); - assertThat(definition.getClassToMock()).isEqualTo(ExampleService.class); + assertThat(definition.getTypeToMock()).isEqualTo(EXAMPLE_SERVICE_TYPE); assertThat(definition.getExtraInterfaces()) .containsExactly(ExampleExtraInterface.class); assertThat(definition.getAnswer()).isEqualTo(Answers.RETURNS_SMART_NULLS); @@ -74,7 +78,7 @@ public class MockDefinitionTests { @Test public void createMock() throws Exception { - MockDefinition definition = new MockDefinition("name", ExampleService.class, + MockDefinition definition = new MockDefinition("name", EXAMPLE_SERVICE_TYPE, new Class[] { ExampleExtraInterface.class }, Answers.RETURNS_SMART_NULLS, true, MockReset.BEFORE, true); ExampleService mock = definition.createMock(); 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 243b24ced6..fb738f52ac 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 @@ -26,6 +26,7 @@ import org.mockito.mock.MockCreationSettings; 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.core.ResolvableType; import static org.assertj.core.api.Assertions.assertThat; @@ -36,39 +37,41 @@ import static org.assertj.core.api.Assertions.assertThat; */ public class SpyDefinitionTests { + private static final ResolvableType REAL_SERVICE_TYPE = ResolvableType + .forClass(RealExampleService.class); + @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void classToSpyMustNotBeNull() throws Exception { this.thrown.expect(IllegalArgumentException.class); - this.thrown.expectMessage("ClassToSpy must not be null"); + this.thrown.expectMessage("TypeToSpy must not be null"); new SpyDefinition(null, null, null, true); } @Test public void createWithDefaults() throws Exception { - SpyDefinition definition = new SpyDefinition(null, RealExampleService.class, null, - true); + SpyDefinition definition = new SpyDefinition(null, REAL_SERVICE_TYPE, null, true); assertThat(definition.getName()).isNull(); - assertThat(definition.getClassToSpy()).isEqualTo(RealExampleService.class); + assertThat(definition.getTypeToSpy()).isEqualTo(REAL_SERVICE_TYPE); assertThat(definition.getReset()).isEqualTo(MockReset.AFTER); assertThat(definition.isProxyTargetAware()).isTrue(); } @Test public void createExplicit() throws Exception { - SpyDefinition definition = new SpyDefinition("name", RealExampleService.class, + SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE, MockReset.BEFORE, false); assertThat(definition.getName()).isEqualTo("name"); - assertThat(definition.getClassToSpy()).isEqualTo(RealExampleService.class); + assertThat(definition.getTypeToSpy()).isEqualTo(REAL_SERVICE_TYPE); assertThat(definition.getReset()).isEqualTo(MockReset.BEFORE); assertThat(definition.isProxyTargetAware()).isFalse(); } @Test public void createSpy() throws Exception { - SpyDefinition definition = new SpyDefinition("name", RealExampleService.class, + SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE, MockReset.BEFORE, true); RealExampleService spy = definition.createSpy(new RealExampleService("hello")); MockCreationSettings settings = new MockUtil().getMockSettings(spy); @@ -81,7 +84,7 @@ public class SpyDefinitionTests { @Test public void createSpyWhenNullInstanceShouldThrowException() throws Exception { - SpyDefinition definition = new SpyDefinition("name", RealExampleService.class, + SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE, MockReset.BEFORE, true); this.thrown.expect(IllegalArgumentException.class); this.thrown.expectMessage("Instance must not be null"); @@ -90,7 +93,7 @@ public class SpyDefinitionTests { @Test public void createSpyWhenWrongInstanceShouldThrowException() throws Exception { - SpyDefinition definition = new SpyDefinition("name", RealExampleService.class, + SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE, MockReset.BEFORE, true); this.thrown.expect(IllegalArgumentException.class); this.thrown.expectMessage("must be an instance of"); @@ -99,7 +102,7 @@ public class SpyDefinitionTests { @Test public void createSpyTwice() throws Exception { - SpyDefinition definition = new SpyDefinition("name", RealExampleService.class, + SpyDefinition definition = new SpyDefinition("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/ExampleGenericService.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/ExampleGenericService.java new file mode 100644 index 0000000000..961a0d31f5 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/ExampleGenericService.java @@ -0,0 +1,29 @@ +/* + * 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; + +/** + * Example service interface for mocking tests. + * + * @param The generic type + * @author Phillip Webb + */ +public interface ExampleGenericService { + + T greeting(); + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/ExampleGenericServiceCaller.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/ExampleGenericServiceCaller.java new file mode 100644 index 0000000000..794dd09931 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/ExampleGenericServiceCaller.java @@ -0,0 +1,49 @@ +/* + * 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; + +/** + * Example bean for mocking tests that calls {@link ExampleGenericService}. + * + * @author Phillip Webb + */ +public class ExampleGenericServiceCaller { + + private final ExampleGenericService integerService; + + private final ExampleGenericService stringService; + + public ExampleGenericServiceCaller(ExampleGenericService integerService, + ExampleGenericService stringService) { + this.integerService = integerService; + this.stringService = stringService; + } + + public ExampleGenericService getIntegerService() { + return this.integerService; + } + + public ExampleGenericService getStringService() { + return this.stringService; + } + + public String sayGreeting() { + return "I say " + +this.integerService.greeting() + " " + + this.stringService.greeting(); + } + +}