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 new file mode 100644 index 00000000000..916414d46ca --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/Definition.java @@ -0,0 +1,79 @@ +/* + * 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.springframework.util.ObjectUtils; + +/** + * Base class for {@link MockDefinition} and {@link SpyDefinition}. + * + * @author Phillip Webb + * @see DefinitionsParser + */ +abstract class Definition { + + private static final int MULTIPLIER = 31; + + private final String name; + + private final MockReset reset; + + Definition(String name, MockReset reset) { + this.name = name; + this.reset = (reset != null ? reset : MockReset.AFTER); + } + + /** + * Return the name for bean. + * @return the name or {@code null} + */ + public String getName() { + return this.name; + } + + /** + * Return the mock reset mode. + * @return the reset mode + */ + public MockReset getReset() { + return this.reset; + } + + @Override + public int hashCode() { + int result = 1; + result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.name); + result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.reset); + return result; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !getClass().isAssignableFrom(obj.getClass())) { + return false; + } + Definition other = (Definition) obj; + boolean result = true; + result &= ObjectUtils.nullSafeEquals(this.name, other.name); + result &= ObjectUtils.nullSafeEquals(this.reset, other.reset); + return result; + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockDefinitionsParser.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/DefinitionsParser.java similarity index 52% rename from spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockDefinitionsParser.java rename to spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/DefinitionsParser.java index 06fac9c8a47..1ad2e171289 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockDefinitionsParser.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/DefinitionsParser.java @@ -33,24 +33,25 @@ import org.springframework.util.ReflectionUtils.FieldCallback; import org.springframework.util.StringUtils; /** - * Parser to create {@link MockDefinition} from {@link MockBean @MockBean} annotations - * declared on or in a class. + * Parser to create {@link MockDefinition} and {@link SpyDefinition} instances from + * {@link MockBean @MockBean} and {@link SpyBean @SpyBean} annotations declared on or in a + * class. * * @author Phillip Webb */ -class MockDefinitionsParser { +class DefinitionsParser { - private final Set definitions; + private final Set definitions; - private final Map fields; + private final Map definitionFields; - MockDefinitionsParser() { - this(Collections.emptySet()); + DefinitionsParser() { + this(Collections.emptySet()); } - MockDefinitionsParser(Collection existing) { - this.definitions = new LinkedHashSet(); - this.fields = new LinkedHashMap(); + DefinitionsParser(Collection existing) { + this.definitions = new LinkedHashSet(); + this.definitionFields = new LinkedHashMap(); if (existing != null) { this.definitions.addAll(existing); } @@ -72,12 +73,16 @@ class MockDefinitionsParser { private void parseElement(AnnotatedElement element) { for (MockBean annotation : AnnotationUtils.getRepeatableAnnotations(element, MockBean.class, MockBeans.class)) { - parseAnnotation(annotation, element); + parseMockBeanAnnotation(annotation, element); + } + for (SpyBean annotation : AnnotationUtils.getRepeatableAnnotations(element, + SpyBean.class, SpyBeans.class)) { + parseSpyBeanAnnotation(annotation, element); } } - private void parseAnnotation(MockBean annotation, AnnotatedElement element) { - Set> classesToMock = getOrDeduceClassesToMock(annotation, element); + 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); if (StringUtils.hasLength(annotation.name())) { @@ -88,30 +93,50 @@ class MockDefinitionsParser { MockDefinition definition = new MockDefinition(annotation.name(), classToMock, annotation.extraInterfaces(), annotation.answer(), annotation.serializable(), annotation.reset()); - boolean isNewDefinition = this.definitions.add(definition); - Assert.state(isNewDefinition, "Duplicate mock definition " + definition); - if (element instanceof Field) { - this.fields.put(definition, (Field) element); - } + addDefinition(element, definition, "mock"); + } + } + + 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); + if (StringUtils.hasLength(annotation.name())) { + Assert.state(classesToSpy.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, + annotation.reset()); + addDefinition(element, definition, "spy"); + } + } + + private void addDefinition(AnnotatedElement element, Definition definition, + String type) { + boolean isNewDefinition = this.definitions.add(definition); + Assert.state(isNewDefinition, "Duplicate " + type + " definition " + definition); + if (element instanceof Field) { + Field field = (Field) element; + this.definitionFields.put(definition, field); } } - private Set> getOrDeduceClassesToMock(MockBean annotation, - AnnotatedElement element) { + private Set> getOrDeduceClasses(AnnotatedElement element, Class[] value) { Set> classes = new LinkedHashSet>(); - classes.addAll(Arrays.asList(annotation.value())); + classes.addAll(Arrays.asList(value)); if (classes.isEmpty() && element instanceof Field) { classes.add(((Field) element).getType()); } return classes; } - public Set getDefinitions() { + public Set getDefinitions() { return Collections.unmodifiableSet(this.definitions); } - public Field getField(MockDefinition definition) { - return this.fields.get(definition); + public Field getField(Definition definition) { + return this.definitionFields.get(definition); } } 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 aa019af7b3a..ef53d39d99e 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 @@ -82,9 +82,9 @@ import org.springframework.test.context.junit4.SpringRunner; public @interface MockBean { /** - * The name of the bean that should be registered with the application context. If not - * specified the name will either be generated or, if the mock replaces an existing - * bean, the existing name will be used. + * The name of the bean to register or replace. If not specified the name will either + * be generated or, if the mock replaces an existing bean, the existing name will be + * used. * @return the name of the bean */ String name() default ""; 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 b3faecdfcef..6a681b5974f 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 @@ -35,12 +35,10 @@ import org.springframework.util.StringUtils; * * @author Phillip Webb */ -class MockDefinition { +class MockDefinition extends Definition { private static final int MULTIPLIER = 31; - private final String name; - private final Class classToMock; private final Set> extraInterfaces; @@ -49,21 +47,18 @@ class MockDefinition { private final boolean serializable; - private final MockReset reset; - MockDefinition(Class classToMock) { this(null, classToMock, null, null, false, null); } MockDefinition(String name, Class classToMock, Class[] extraInterfaces, Answers answer, boolean serializable, MockReset reset) { + super(name, reset); Assert.notNull(classToMock, "ClassToMock must not be null"); - this.name = name; this.classToMock = classToMock; this.extraInterfaces = asClassSet(extraInterfaces); this.answer = (answer != null ? answer : Answers.RETURNS_DEFAULTS); this.serializable = serializable; - this.reset = (reset != null ? reset : MockReset.AFTER); } private Set> asClassSet(Class[] classes) { @@ -74,14 +69,6 @@ class MockDefinition { return Collections.unmodifiableSet(classSet); } - /** - * Return the name for bean. - * @return the name or {@code null} - */ - public String getName() { - return this.name; - } - /** * Return the classes that should be mocked. * @return the class to mock; never {@code null} @@ -114,23 +101,13 @@ class MockDefinition { return this.serializable; } - /** - * Return the mock reset mode. - * @return the reset mode - */ - public MockReset getReset() { - return this.reset; - } - @Override public int hashCode() { - int result = 1; - result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.name); + int result = super.hashCode(); result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.classToMock); result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.extraInterfaces); result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.answer); result = MULTIPLIER * result + (this.serializable ? 1231 : 1237); - result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.reset); return result; } @@ -143,32 +120,30 @@ class MockDefinition { return false; } MockDefinition other = (MockDefinition) obj; - boolean result = true; - result &= ObjectUtils.nullSafeEquals(this.name, other.name); + boolean result = super.equals(obj); result &= ObjectUtils.nullSafeEquals(this.classToMock, other.classToMock); result &= ObjectUtils.nullSafeEquals(this.extraInterfaces, other.extraInterfaces); result &= ObjectUtils.nullSafeEquals(this.answer, other.answer); result &= this.serializable == other.serializable; - result &= ObjectUtils.nullSafeEquals(this.reset, other.reset); return result; } @Override public String toString() { - return new ToStringCreator(this).append("name", this.name) + return new ToStringCreator(this).append("name", getName()) .append("classToMock", this.classToMock) .append("extraInterfaces", this.extraInterfaces) .append("answer", this.answer).append("serializable", this.serializable) - .append("reset", this.reset).toString(); + .append("reset", getReset()).toString(); } public T createMock() { - return createMock(this.name); + return createMock(getName()); } @SuppressWarnings("unchecked") public T createMock(String name) { - MockSettings settings = MockReset.withSettings(this.reset); + MockSettings settings = MockReset.withSettings(getReset()); if (StringUtils.hasLength(name)) { settings.name(name); } diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizer.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizer.java index 965ba888279..f2c31ad0839 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizer.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizer.java @@ -16,6 +16,7 @@ package org.springframework.boot.test.mock.mockito; +import java.util.LinkedHashSet; import java.util.Set; import org.springframework.beans.factory.support.BeanDefinitionRegistry; @@ -30,10 +31,10 @@ import org.springframework.test.context.MergedContextConfiguration; */ class MockitoContextCustomizer implements ContextCustomizer { - private final Set definitions; + private final Set definitions; - MockitoContextCustomizer(Set definitions) { - this.definitions = definitions; + MockitoContextCustomizer(Set definitions) { + this.definitions = new LinkedHashSet(definitions); } @Override diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizerFactory.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizerFactory.java index 689302f796b..fe1f68c7c55 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizerFactory.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizerFactory.java @@ -34,7 +34,7 @@ class MockitoContextCustomizerFactory implements ContextCustomizerFactory { List configAttributes) { // We gather the explicit mock definitions here since they form part of the // MergedContextConfiguration key. Different mocks need to have a different key - MockDefinitionsParser parser = new MockDefinitionsParser(); + DefinitionsParser parser = new DefinitionsParser(); parser.parse(testClass); return new MockitoContextCustomizer(parser.getDefinitions()); } 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 61abdefeb81..da22ed41acf 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 @@ -34,6 +34,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +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; @@ -66,7 +67,7 @@ import org.springframework.util.StringUtils; */ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements BeanClassLoaderAware, BeanFactoryAware, BeanFactoryPostProcessor, - Ordered { + BeanPostProcessor, Ordered { private static final String BEAN_NAME = MockitoPostProcessor.class.getName(); @@ -74,7 +75,7 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda .getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass"); - private final Set mockDefinitions; + private final Set definitions; private ClassLoader classLoader; @@ -82,17 +83,19 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda private final BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator(); - private Map beanNameRegistry = new HashMap(); + private Map beanNameRegistry = new HashMap(); private Map fieldRegistry = new HashMap(); + private Map spies = new HashMap(); + /** * Create a new {@link MockitoPostProcessor} instance with the given initial * definitions. - * @param mockDefinitions the initial definitions + * @param definitions the initial definitions */ - public MockitoPostProcessor(Set mockDefinitions) { - this.mockDefinitions = mockDefinitions; + public MockitoPostProcessor(Set definitions) { + this.definitions = definitions; } @Override @@ -118,14 +121,14 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda private void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory, BeanDefinitionRegistry registry) { - MockDefinitionsParser parser = new MockDefinitionsParser(this.mockDefinitions); + DefinitionsParser parser = new DefinitionsParser(this.definitions); for (Class configurationClass : getConfigurationClasses(beanFactory)) { parser.parse(configurationClass); } - Set definitions = parser.getDefinitions(); - for (MockDefinition definition : definitions) { + Set definitions = parser.getDefinitions(); + for (Definition definition : definitions) { Field field = parser.getField(definition); - registerMock(beanFactory, registry, definition, field); + register(beanFactory, registry, definition, field); } } @@ -152,20 +155,23 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda return definitions; } - void inject(Field field, Object target, MockDefinition definition) { - String beanName = this.beanNameRegistry.get(definition); - Assert.state(StringUtils.hasLength(beanName), - "No mock found for definition " + definition); - injectMock(field, target, beanName); + private void register(ConfigurableListableBeanFactory beanFactory, + BeanDefinitionRegistry registry, Definition definition, Field field) { + if (definition instanceof MockDefinition) { + registerMock(beanFactory, registry, (MockDefinition) definition, field); + } + else if (definition instanceof SpyDefinition) { + registerSpy(beanFactory, registry, (SpyDefinition) definition, field); + } } private void registerMock(ConfigurableListableBeanFactory beanFactory, - BeanDefinitionRegistry registry, MockDefinition mockDefinition, Field field) { - RootBeanDefinition beanDefinition = createBeanDefinition(mockDefinition); - String name = getBeanName(beanFactory, registry, mockDefinition, beanDefinition); + BeanDefinitionRegistry registry, MockDefinition definition, Field field) { + RootBeanDefinition beanDefinition = createBeanDefinition(definition); + String name = getBeanName(beanFactory, registry, definition, beanDefinition); beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(1, name); registry.registerBeanDefinition(name, beanDefinition); - this.beanNameRegistry.put(mockDefinition, name); + this.beanNameRegistry.put(definition, name); if (field != null) { this.fieldRegistry.put(field, name); } @@ -184,12 +190,12 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda /** * Factory method used by defined beans to actually create the mock. - * @param definition the mock definition + * @param mockDefinition the mock definition * @param name the bean name * @return the mock instance */ - protected final Object createMock(MockDefinition definition, String name) { - return definition.createMock(name + " bean"); + protected final Object createMock(MockDefinition mockDefinition, String name) { + return mockDefinition.createMock(name + " bean"); } private String getBeanName(ConfigurableListableBeanFactory beanFactory, @@ -212,6 +218,60 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda + new TreeSet(Arrays.asList(existingBeans))); } + private void registerSpy(ConfigurableListableBeanFactory beanFactory, + BeanDefinitionRegistry registry, SpyDefinition spyDefinition, Field field) { + String[] existingBeans = beanFactory + .getBeanNamesForType(spyDefinition.getClassToSpy()); + if (ObjectUtils.isEmpty(existingBeans)) { + createSpy(registry, spyDefinition, field); + } + else { + registerSpies(spyDefinition, field, existingBeans); + } + } + + private void createSpy(BeanDefinitionRegistry registry, SpyDefinition spyDefinition, + Field field) { + RootBeanDefinition beanDefinition = new RootBeanDefinition( + spyDefinition.getClassToSpy()); + String beanName = this.beanNameGenerator.generateBeanName(beanDefinition, + registry); + registry.registerBeanDefinition(beanName, beanDefinition); + registerSpy(spyDefinition, field, beanName); + } + + private void registerSpies(SpyDefinition spyDefinition, Field field, + String[] existingBeans) { + if (field != null) { + Assert.state(field == null || existingBeans.length == 1, + "Unable to register spy bean " + + spyDefinition.getClassToSpy().getName() + + " expected a single existing bean to replace but found " + + new TreeSet(Arrays.asList(existingBeans))); + } + for (String beanName : existingBeans) { + registerSpy(spyDefinition, field, beanName); + } + } + + private void registerSpy(SpyDefinition spyDefinition, Field field, String beanName) { + this.spies.put(beanName, spyDefinition); + this.beanNameRegistry.put(spyDefinition, beanName); + if (field != null) { + this.fieldRegistry.put(field, beanName); + } + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) + throws BeansException { + SpyDefinition spyDefinition = this.spies.get(beanName); + if (spyDefinition != null) { + bean = spyDefinition.createSpy(beanName, bean); + } + return bean; + } + @Override public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, final Object bean, String beanName) @@ -231,18 +291,27 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda private void postProcessField(Object bean, Field field) { String beanName = this.fieldRegistry.get(field); if (StringUtils.hasLength(beanName)) { - injectMock(field, bean, beanName); + inject(field, bean, beanName); } } - private void injectMock(Field field, Object target, String beanName) { + void inject(Field field, Object target, Definition definition) { + String beanName = this.beanNameRegistry.get(definition); + Assert.state(StringUtils.hasLength(beanName), + "No bean found for definition " + definition); + inject(field, target, beanName); + } + + private void inject(Field field, Object target, String beanName) { try { field.setAccessible(true); - Object mockBean = this.beanFactory.getBean(beanName, field.getType()); - ReflectionUtils.setField(field, target, mockBean); + Assert.state(ReflectionUtils.getField(field, target) == null, + "The field " + field + " cannot have an existing value"); + Object bean = this.beanFactory.getBean(beanName, field.getType()); + ReflectionUtils.setField(field, target, bean); } catch (Throwable ex) { - throw new BeanCreationException("Could not inject mock field: " + field, ex); + throw new BeanCreationException("Could not inject field: " + field, ex); } } @@ -264,11 +333,11 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda * Register the processor with a {@link BeanDefinitionRegistry}. Not required when * using the {@link SpringRunner} as registration is automatic. * @param registry the bean definition registry - * @param mockDefinitions the initial mock definitions + * @param definitions the initial mock/spy definitions */ public static void register(BeanDefinitionRegistry registry, - Set mockDefinitions) { - register(registry, MockitoPostProcessor.class, mockDefinitions); + Set definitions) { + register(registry, MockitoPostProcessor.class, definitions); } /** @@ -276,18 +345,18 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda * using the {@link SpringRunner} as registration is automatic. * @param registry the bean definition registry * @param postProcessor the post processor class to register - * @param mockDefinitions the initial mock definitions + * @param definitions the initial mock/spy definitions */ @SuppressWarnings("unchecked") public static void register(BeanDefinitionRegistry registry, Class postProcessor, - Set mockDefinitions) { + Set definitions) { BeanDefinition definition = getOrAddBeanDefinition(registry, postProcessor); ValueHolder constructorArg = definition.getConstructorArgumentValues() .getIndexedArgumentValue(0, Set.class); - Set existing = (Set) constructorArg.getValue(); - if (mockDefinitions != null) { - existing.addAll(mockDefinitions); + Set existing = (Set) constructorArg.getValue(); + if (definitions != null) { + existing.addAll(definitions); } } diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoInitializeTestExecutionListener.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListener.java similarity index 91% rename from spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoInitializeTestExecutionListener.java rename to spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListener.java index ff200e63a4e..7e488cae580 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoInitializeTestExecutionListener.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListener.java @@ -38,10 +38,11 @@ import org.springframework.util.ReflectionUtils.FieldCallback; * * @author Phillip Webb */ -class MockitoInitializeTestExecutionListener extends AbstractTestExecutionListener { +class MockitoTestExecutionListener extends AbstractTestExecutionListener { @Override public void prepareTestInstance(TestContext testContext) throws Exception { + System.out.println("Prepare"); if (hasMockitoAnnotations(testContext)) { MockitoAnnotations.initMocks(testContext.getTestInstance()); } @@ -55,18 +56,18 @@ class MockitoInitializeTestExecutionListener extends AbstractTestExecutionListen } private void injectFields(TestContext testContext) { - MockDefinitionsParser parser = new MockDefinitionsParser(); + DefinitionsParser parser = new DefinitionsParser(); parser.parse(testContext.getTestClass()); if (!parser.getDefinitions().isEmpty()) { injectFields(testContext, parser); } } - private void injectFields(TestContext testContext, MockDefinitionsParser parser) { + private void injectFields(TestContext testContext, DefinitionsParser parser) { ApplicationContext applicationContext = testContext.getApplicationContext(); MockitoPostProcessor postProcessor = applicationContext .getBean(MockitoPostProcessor.class); - for (MockDefinition definition : parser.getDefinitions()) { + for (Definition definition : parser.getDefinitions()) { Field field = parser.getField(definition); if (field != null) { postProcessor.inject(field, testContext.getTestInstance(), definition); 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 new file mode 100644 index 00000000000..28137757d5a --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpyBean.java @@ -0,0 +1,119 @@ +/* + * 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.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.runner.RunWith; + +import org.springframework.context.ApplicationContext; +import org.springframework.core.annotation.AliasFor; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Annotation that can be used to appy Mockto spies to a Spring + * {@link ApplicationContext}. Can be used as a class level annotation or on fields in + * either {@code @Configuration} classes, or test classes that are + * {@link RunWith @RunWith} the {@link SpringRunner}. + *

+ * Spies can be applied by type or by {@link #name() bean name}. All beans in the context + * of the same type will be wrapped with the spy, if no existing bean is defined a new one + * will be added. + *

+ * When {@code @SpyBean} is used on a field, as well as being registered in the + * application context, the spy will also be injected into the field. Typical usage might + * be:

+ * @RunWith(SpringRunner.class)
+ * public class ExampleTests {
+ *
+ *     @SpyBean
+ *     private ExampleService service;
+ *
+ *     @Autowired
+ *     private UserOfService userOfService;
+ *
+ *     @Test
+ *     public void testUserOfService() {
+ *         String actual = this.userOfService.makeUse();
+ *         assertEquals("Was: Hello", actual);
+ *         verify(this.service).greet();
+ *     }
+ *
+ *     @Configuration
+ *     @Import(UserOfService.class) // A @Component injected with ExampleService
+ *     static class Config {
+ *     }
+ *
+ *
+ * }
+ * 
+ *

+ * This annotation is {@code @Repeatable} and may be specified multiple times when working + * with Java 8 or contained within an {@link SpyBeans @SpyBeans} annotation. + * + * @author Phillip Webb + * @since 1.4.0 + * @see MockitoPostProcessor + */ +@Target({ ElementType.TYPE, ElementType.FIELD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Repeatable(SpyBeans.class) +public @interface SpyBean { + + /** + * The name of the bean to spy. If not specified the name will either be generated or, + * if the spy is for an existing bean, the existing name will be used. + * @return the name of the bean + */ + String name() default ""; + + /** + * The classes to spy. This is an alias of {@link #classes()} which can be used for + * brevity if no other attributes are defined. See {@link #classes()} for details. + * @return the classes to mock + */ + @AliasFor("classes") + Class[] value() default {}; + + /** + * 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 @MockBean} also defines a {@code name} this attribute can only contain + * a single value. + *

+ * If this is the only attribute specified consider using the {@code value} alias + * instead. + * @return the classes to mock + */ + @AliasFor("value") + Class[] classes() default {}; + + /** + * The reset mode to apply to the spied bean. The default is {@link MockReset#AFTER} + * meaning that spies are automatically reset after each test method is invoked. + * @return the reset mode + */ + MockReset reset() default MockReset.AFTER; + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpyBeans.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpyBeans.java new file mode 100644 index 00000000000..dba8929cb6c --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpyBeans.java @@ -0,0 +1,47 @@ +/* + * 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.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Container annotation that aggregates several {@link SpyBean} annotations. + *

+ * Can be used natively, declaring several nested {@link SpyBean} annotations. Can also be + * used in conjunction with Java 8's support for repeatable annotations, where + * {@link SpyBean} can simply be declared several times on the same + * {@linkplain ElementType#TYPE type}, implicitly generating this container annotation. + * + * @author Phillip Webb + * @since 1.4.0 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Documented +public @interface SpyBeans { + + /** + * Return the contained {@link SpyBean} annotations. + * @return the spy beans + */ + SpyBean[] value(); + +} 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 new file mode 100644 index 00000000000..2409cc6045e --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpyDefinition.java @@ -0,0 +1,100 @@ +/* + * 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.mockito.MockSettings; +import org.mockito.Mockito; +import org.mockito.internal.util.MockUtil; + +import org.springframework.core.style.ToStringCreator; +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; + +/** + * A complete definition that can be used to create a Mockito spy. + * + * @author Phillip Webb + */ +class SpyDefinition extends Definition { + + private MockUtil mockUtil = new MockUtil(); + + private static final int MULTIPLIER = 31; + + private final Class classToSpy; + + SpyDefinition(String name, Class classToSpy, MockReset reset) { + super(name, reset); + Assert.notNull(classToSpy, "ClassToSpy must not be null"); + this.classToSpy = classToSpy; + + } + + public Class getClassToSpy() { + return this.classToSpy; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.classToSpy); + return result; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || obj.getClass() != getClass()) { + return false; + } + SpyDefinition other = (SpyDefinition) obj; + boolean result = super.equals(obj); + result &= ObjectUtils.nullSafeEquals(this.classToSpy, other.classToSpy); + return result; + } + + @Override + public String toString() { + return new ToStringCreator(this).append("name", getName()) + .append("classToSpy", this.classToSpy).append("reset", getReset()) + .toString(); + } + + public T createSpy(Object instance) { + return createSpy(getName(), instance); + } + + @SuppressWarnings("unchecked") + public T createSpy(String name, Object instance) { + Assert.notNull(instance, "Instance must not be null"); + Assert.isInstanceOf(this.classToSpy, instance); + if (this.mockUtil.isSpy(instance)) { + return (T) instance; + } + MockSettings settings = MockReset.withSettings(getReset()); + if (StringUtils.hasLength(name)) { + settings.name(name); + } + settings.spiedInstance(instance); + settings.defaultAnswer(Mockito.CALLS_REAL_METHODS); + return (T) Mockito.mock(instance.getClass(), settings); + } + +} diff --git a/spring-boot-test/src/main/resources/META-INF/spring.factories b/spring-boot-test/src/main/resources/META-INF/spring.factories index 913523eb46b..b1608c6be26 100644 --- a/spring-boot-test/src/main/resources/META-INF/spring.factories +++ b/spring-boot-test/src/main/resources/META-INF/spring.factories @@ -7,5 +7,5 @@ org.springframework.boot.test.mock.mockito.MockitoContextCustomizerFactory # Test Execution Listeners org.springframework.test.context.TestExecutionListener=\ -org.springframework.boot.test.mock.mockito.MockitoInitializeTestExecutionListener,\ +org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener,\ org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener 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 new file mode 100644 index 00000000000..98002d11656 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/DefinitionsParserTests.java @@ -0,0 +1,292 @@ +/* + * 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.util.ArrayList; +import java.util.List; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Answers; + +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link DefinitionsParser}. + * + * @author Phillip Webb + */ +public class DefinitionsParserTests { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private DefinitionsParser parser = new DefinitionsParser(); + + @Test + public void parseSingleMockBean() { + this.parser.parse(SingleMockBean.class); + assertThat(getDefinitions()).hasSize(1); + assertThat(getMockDefinition(0).getClassToMock()).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()) + .isEqualTo(ExampleServiceCaller.class); + } + + @Test + public void parseMockBeanAttributes() throws Exception { + this.parser.parse(MockBeanAttributes.class); + assertThat(getDefinitions()).hasSize(1); + MockDefinition definition = getMockDefinition(0); + assertThat(definition.getName()).isEqualTo("Name"); + assertThat(definition.getClassToMock()).isEqualTo(ExampleService.class); + assertThat(definition.getExtraInterfaces()) + .containsExactly(ExampleExtraInterface.class); + assertThat(definition.getAnswer()).isEqualTo(Answers.RETURNS_SMART_NULLS); + assertThat(definition.isSerializable()).isEqualTo(true); + assertThat(definition.getReset()).isEqualTo(MockReset.NONE); + } + + @Test + 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()) + .isEqualTo(ExampleServiceCaller.class); + } + + @Test + public void parseMockBeanInferClassToMock() throws Exception { + this.parser.parse(MockBeanInferClassToMock.class); + assertThat(getDefinitions()).hasSize(1); + assertThat(getMockDefinition(0).getClassToMock()).isEqualTo(ExampleService.class); + } + + @Test + public void parseMockBeanMissingClassToMock() throws Exception { + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage("Unable to deduce class to mock"); + this.parser.parse(MockBeanMissingClassToMock.class); + } + + @Test + 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()) + .isEqualTo(ExampleServiceCaller.class); + } + + @Test + public void parseMockBeanMultipleClassesWithName() throws Exception { + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage( + "The name attribute can only be used when mocking a single class"); + this.parser.parse(MockBeanMultipleClassesWithName.class); + } + + @Test + public void parseSingleSpyBean() { + this.parser.parse(SingleSpyBean.class); + assertThat(getDefinitions()).hasSize(1); + assertThat(getSpyDefinition(0).getClassToSpy()) + .isEqualTo(RealExampleService.class); + } + + @Test + public void parseRepeatSpyBean() { + this.parser.parse(RepeatSpyBean.class); + assertThat(getDefinitions()).hasSize(2); + assertThat(getSpyDefinition(0).getClassToSpy()) + .isEqualTo(RealExampleService.class); + assertThat(getSpyDefinition(1).getClassToSpy()) + .isEqualTo(ExampleServiceCaller.class); + } + + @Test + public void parseSpyBeanAttributes() throws Exception { + this.parser.parse(SpyBeanAttributes.class); + assertThat(getDefinitions()).hasSize(1); + SpyDefinition definition = getSpyDefinition(0); + assertThat(definition.getName()).isEqualTo("Name"); + assertThat(definition.getClassToSpy()).isEqualTo(RealExampleService.class); + assertThat(definition.getReset()).isEqualTo(MockReset.NONE); + } + + @Test + public void parseSpyBeanOnClassAndField() throws Exception { + this.parser.parse(SpyBeanOnClassAndField.class); + assertThat(getDefinitions()).hasSize(2); + assertThat(getSpyDefinition(0).getClassToSpy()) + .isEqualTo(RealExampleService.class); + assertThat(getSpyDefinition(1).getClassToSpy()) + .isEqualTo(ExampleServiceCaller.class); + } + + @Test + public void parseSpyBeanInferClassToMock() throws Exception { + this.parser.parse(SpyBeanInferClassToMock.class); + assertThat(getDefinitions()).hasSize(1); + assertThat(getSpyDefinition(0).getClassToSpy()) + .isEqualTo(RealExampleService.class); + } + + @Test + public void parseSpyBeanMissingClassToMock() throws Exception { + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage("Unable to deduce class to spy"); + this.parser.parse(SpyBeanMissingClassToMock.class); + } + + @Test + public void parseSpyBeanMultipleClasses() throws Exception { + this.parser.parse(SpyBeanMultipleClasses.class); + assertThat(getDefinitions()).hasSize(2); + assertThat(getSpyDefinition(0).getClassToSpy()) + .isEqualTo(RealExampleService.class); + assertThat(getSpyDefinition(1).getClassToSpy()) + .isEqualTo(ExampleServiceCaller.class); + } + + @Test + public void parseSpyBeanMultipleClassesWithName() throws Exception { + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage( + "The name attribute can only be used when spying a single class"); + this.parser.parse(SpyBeanMultipleClassesWithName.class); + } + + private MockDefinition getMockDefinition(int index) { + return (MockDefinition) getDefinitions().get(index); + } + + private SpyDefinition getSpyDefinition(int index) { + return (SpyDefinition) getDefinitions().get(index); + } + + private List getDefinitions() { + return new ArrayList(this.parser.getDefinitions()); + } + + @MockBean(ExampleService.class) + static class SingleMockBean { + + } + + @MockBeans({ @MockBean(ExampleService.class), @MockBean(ExampleServiceCaller.class) }) + static class RepeatMockBean { + + } + + @MockBean(name = "Name", classes = ExampleService.class, extraInterfaces = ExampleExtraInterface.class, answer = Answers.RETURNS_SMART_NULLS, serializable = true, reset = MockReset.NONE) + static class MockBeanAttributes { + + } + + @MockBean(ExampleService.class) + static class MockBeanOnClassAndField { + + @MockBean(ExampleServiceCaller.class) + private Object caller; + + } + + @MockBean({ ExampleService.class, ExampleServiceCaller.class }) + static class MockBeanMultipleClasses { + + } + + @MockBean(name = "name", classes = { ExampleService.class, + ExampleServiceCaller.class }) + static class MockBeanMultipleClassesWithName { + + } + + static class MockBeanInferClassToMock { + + @MockBean + private ExampleService exampleService; + + } + + @MockBean + static class MockBeanMissingClassToMock { + + } + + @SpyBean(RealExampleService.class) + static class SingleSpyBean { + + } + + @SpyBeans({ @SpyBean(RealExampleService.class), + @SpyBean(ExampleServiceCaller.class) }) + static class RepeatSpyBean { + + } + + @SpyBean(name = "Name", classes = RealExampleService.class, reset = MockReset.NONE) + static class SpyBeanAttributes { + + } + + @SpyBean(RealExampleService.class) + static class SpyBeanOnClassAndField { + + @SpyBean(ExampleServiceCaller.class) + private Object caller; + + } + + @SpyBean({ RealExampleService.class, ExampleServiceCaller.class }) + static class SpyBeanMultipleClasses { + + } + + @SpyBean(name = "name", classes = { RealExampleService.class, + ExampleServiceCaller.class }) + static class SpyBeanMultipleClassesWithName { + + } + + static class SpyBeanInferClassToMock { + + @SpyBean + private RealExampleService exampleService; + + } + + @SpyBean + static class SpyBeanMissingClassToMock { + + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationClassForExistingBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnConfigurationClassForExistingBeanIntegrationTests.java similarity index 96% rename from spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationClassForExistingBeanIntegrationTests.java rename to spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnConfigurationClassForExistingBeanIntegrationTests.java index d2ded98f30f..4fcbcf9c0c4 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationClassForExistingBeanIntegrationTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnConfigurationClassForExistingBeanIntegrationTests.java @@ -36,7 +36,7 @@ import static org.mockito.BDDMockito.given; * @author Phillip Webb */ @RunWith(SpringRunner.class) -public class OnConfigurationClassForExistingBeanIntegrationTests { +public class MockBeanOnConfigurationClassForExistingBeanIntegrationTests { @Autowired private ExampleServiceCaller caller; diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationClassForNewBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnConfigurationClassForNewBeanIntegrationTests.java similarity index 96% rename from spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationClassForNewBeanIntegrationTests.java rename to spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnConfigurationClassForNewBeanIntegrationTests.java index 8f868a0ca42..05a5a0f47dd 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationClassForNewBeanIntegrationTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnConfigurationClassForNewBeanIntegrationTests.java @@ -36,7 +36,7 @@ import static org.mockito.BDDMockito.given; * @author Phillip Webb */ @RunWith(SpringRunner.class) -public class OnConfigurationClassForNewBeanIntegrationTests { +public class MockBeanOnConfigurationClassForNewBeanIntegrationTests { @Autowired private ExampleServiceCaller caller; diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationFieldForExistingBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnConfigurationFieldForExistingBeanIntegrationTests.java similarity index 96% rename from spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationFieldForExistingBeanIntegrationTests.java rename to spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnConfigurationFieldForExistingBeanIntegrationTests.java index ec5c3bf2138..4108af4fba0 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationFieldForExistingBeanIntegrationTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnConfigurationFieldForExistingBeanIntegrationTests.java @@ -37,7 +37,7 @@ import static org.mockito.BDDMockito.given; * @author Phillip Webb */ @RunWith(SpringRunner.class) -public class OnConfigurationFieldForExistingBeanIntegrationTests { +public class MockBeanOnConfigurationFieldForExistingBeanIntegrationTests { @Autowired private Config config; diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationFieldForNewBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnConfigurationFieldForNewBeanIntegrationTests.java similarity index 96% rename from spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationFieldForNewBeanIntegrationTests.java rename to spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnConfigurationFieldForNewBeanIntegrationTests.java index 449b4d54c3d..2366542bd2a 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationFieldForNewBeanIntegrationTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnConfigurationFieldForNewBeanIntegrationTests.java @@ -36,7 +36,7 @@ import static org.mockito.BDDMockito.given; * @author Phillip Webb */ @RunWith(SpringRunner.class) -public class OnConfigurationFieldForNewBeanIntegrationTests { +public class MockBeanOnConfigurationFieldForNewBeanIntegrationTests { @Autowired private Config config; diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnContextHierarchyIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnContextHierarchyIntegrationTests.java similarity index 91% rename from spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnContextHierarchyIntegrationTests.java rename to spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnContextHierarchyIntegrationTests.java index de0318c251e..0e81a91f180 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnContextHierarchyIntegrationTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnContextHierarchyIntegrationTests.java @@ -21,8 +21,8 @@ import org.junit.runner.RunWith; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.OnContextHierarchyIntegrationTests.ChildConfig; -import org.springframework.boot.test.mock.mockito.OnContextHierarchyIntegrationTests.ParentConfig; +import org.springframework.boot.test.mock.mockito.MockBeanOnContextHierarchyIntegrationTests.ChildConfig; +import org.springframework.boot.test.mock.mockito.MockBeanOnContextHierarchyIntegrationTests.ParentConfig; import org.springframework.boot.test.mock.mockito.example.ExampleService; import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller; import org.springframework.context.ApplicationContext; @@ -42,7 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) @ContextHierarchy({ @ContextConfiguration(classes = ParentConfig.class), @ContextConfiguration(classes = ChildConfig.class) }) -public class OnContextHierarchyIntegrationTests { +public class MockBeanOnContextHierarchyIntegrationTests { @Autowired private ChildConfig childConfig; diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestClassForExistingBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestClassForExistingBeanIntegrationTests.java similarity index 96% rename from spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestClassForExistingBeanIntegrationTests.java rename to spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestClassForExistingBeanIntegrationTests.java index f26bcf54063..badc39ce82f 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestClassForExistingBeanIntegrationTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestClassForExistingBeanIntegrationTests.java @@ -37,7 +37,7 @@ import static org.mockito.BDDMockito.given; */ @RunWith(SpringRunner.class) @MockBean(ExampleService.class) -public class OnTestClassForExistingBeanIntegrationTests { +public class MockBeanOnTestClassForExistingBeanIntegrationTests { @Autowired private ExampleServiceCaller caller; diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestClassForNewBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestClassForNewBeanIntegrationTests.java similarity index 96% rename from spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestClassForNewBeanIntegrationTests.java rename to spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestClassForNewBeanIntegrationTests.java index 67c0f73e6a0..2529670749d 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestClassForNewBeanIntegrationTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestClassForNewBeanIntegrationTests.java @@ -36,7 +36,7 @@ import static org.mockito.BDDMockito.given; */ @RunWith(SpringRunner.class) @MockBean(ExampleService.class) -public class OnTestClassForNewBeanIntegrationTests { +public class MockBeanOnTestClassForNewBeanIntegrationTests { @Autowired private ExampleServiceCaller caller; diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForExistingBeanCacheIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestFieldForExistingBeanCacheIntegrationTests.java similarity index 83% rename from spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForExistingBeanCacheIntegrationTests.java rename to spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestFieldForExistingBeanCacheIntegrationTests.java index 95d21e4392f..2e2234395f5 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForExistingBeanCacheIntegrationTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestFieldForExistingBeanCacheIntegrationTests.java @@ -31,15 +31,15 @@ import static org.mockito.BDDMockito.given; /** * Test {@link MockBean} on a test class field can be used to replace existing beans when * the context is cached. This test is identical to - * {@link OnTestFieldForExistingBeanCacheIntegrationTests} so one of them should trigger - * application context caching. + * {@link MockBeanOnTestFieldForExistingBeanIntegrationTests} so one of them should + * trigger application context caching. * * @author Phillip Webb - * @see OnTestFieldForExistingBeanIntegrationTests + * @see MockBeanOnTestFieldForExistingBeanIntegrationTests */ @RunWith(SpringRunner.class) -@ContextConfiguration(classes = OnTestFieldForExistingBeanConfig.class) -public class OnTestFieldForExistingBeanCacheIntegrationTests { +@ContextConfiguration(classes = MockBeanOnTestFieldForExistingBeanConfig.class) +public class MockBeanOnTestFieldForExistingBeanCacheIntegrationTests { @MockBean private ExampleService exampleService; diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForExistingBeanConfig.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestFieldForExistingBeanConfig.java similarity index 81% rename from spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForExistingBeanConfig.java rename to spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestFieldForExistingBeanConfig.java index efd8414d26b..32e3f34ee34 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForExistingBeanConfig.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestFieldForExistingBeanConfig.java @@ -21,14 +21,14 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; /** - * Config for {@link OnTestFieldForExistingBeanIntegrationTests} and - * {@link OnTestFieldForExistingBeanCacheIntegrationTests}. Extracted to a shared config + * Config for {@link MockBeanOnTestFieldForExistingBeanIntegrationTests} and + * {@link MockBeanOnTestFieldForExistingBeanCacheIntegrationTests}. Extracted to a shared config * to trigger caching. * * @author Phillip Webb */ @Configuration @Import(ExampleServiceCaller.class) -public class OnTestFieldForExistingBeanConfig { +public class MockBeanOnTestFieldForExistingBeanConfig { } diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForExistingBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestFieldForExistingBeanIntegrationTests.java similarity index 88% rename from spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForExistingBeanIntegrationTests.java rename to spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestFieldForExistingBeanIntegrationTests.java index 109697defde..a4bbdea2bf0 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForExistingBeanIntegrationTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestFieldForExistingBeanIntegrationTests.java @@ -32,11 +32,11 @@ import static org.mockito.BDDMockito.given; * Test {@link MockBean} on a test class field can be used to replace existing beans. * * @author Phillip Webb - * @see OnTestFieldForExistingBeanCacheIntegrationTests + * @see MockBeanOnTestFieldForExistingBeanCacheIntegrationTests */ @RunWith(SpringRunner.class) -@ContextConfiguration(classes = OnTestFieldForExistingBeanConfig.class) -public class OnTestFieldForExistingBeanIntegrationTests { +@ContextConfiguration(classes = MockBeanOnTestFieldForExistingBeanConfig.class) +public class MockBeanOnTestFieldForExistingBeanIntegrationTests { @MockBean private ExampleService exampleService; diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForNewBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestFieldForNewBeanIntegrationTests.java similarity index 96% rename from spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForNewBeanIntegrationTests.java rename to spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestFieldForNewBeanIntegrationTests.java index b074ed6f14e..5e39d6ef959 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForNewBeanIntegrationTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockBeanOnTestFieldForNewBeanIntegrationTests.java @@ -36,7 +36,7 @@ import static org.mockito.BDDMockito.given; * @author Phillip Webb */ @RunWith(SpringRunner.class) -public class OnTestFieldForNewBeanIntegrationTests { +public class MockBeanOnTestFieldForNewBeanIntegrationTests { @MockBean private ExampleService exampleService; diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockDefinitionsParserTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockDefinitionsParserTests.java deleted file mode 100644 index 176026d0e64..00000000000 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockDefinitionsParserTests.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * 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.util.ArrayList; -import java.util.List; - -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.mockito.Answers; - -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.MyMockBean; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link MockDefinitionsParser}. - * - * @author Phillip Webb - */ -public class MockDefinitionsParserTests { - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - private MockDefinitionsParser parser = new MockDefinitionsParser(); - - @Test - public void parseSingleMockBean() { - this.parser.parse(SingleMockBean.class); - assertThat(getDefinitions()).hasSize(1); - assertThat(getDefinition(0).getClassToMock()).isEqualTo(ExampleService.class); - } - - @Test - public void parseRepeatMockBean() { - this.parser.parse(RepeatMockBean.class); - assertThat(getDefinitions()).hasSize(2); - assertThat(getDefinition(0).getClassToMock()).isEqualTo(ExampleService.class); - assertThat(getDefinition(1).getClassToMock()) - .isEqualTo(ExampleServiceCaller.class); - } - - @Test - @Ignore // See SPR-13973 - public void parseMetaMockBean() { - this.parser.parse(MetaMockBean.class); - assertThat(getDefinitions()).hasSize(1); - assertThat(getDefinition(0).getClassToMock()).isEqualTo(ExampleService.class); - } - - @Test - public void parseMockBeanAttributes() throws Exception { - this.parser.parse(MockBeanAttributes.class); - assertThat(getDefinitions()).hasSize(1); - MockDefinition definition = getDefinition(0); - assertThat(definition.getName()).isEqualTo("Name"); - assertThat(definition.getClassToMock()).isEqualTo(ExampleService.class); - assertThat(definition.getExtraInterfaces()) - .containsExactly(ExampleExtraInterface.class); - assertThat(definition.getAnswer()).isEqualTo(Answers.RETURNS_SMART_NULLS); - assertThat(definition.isSerializable()).isEqualTo(true); - assertThat(definition.getReset()).isEqualTo(MockReset.NONE); - } - - @Test - public void parseOnClassAndField() throws Exception { - this.parser.parse(OnClassAndField.class); - assertThat(getDefinitions()).hasSize(2); - assertThat(getDefinition(0).getClassToMock()).isEqualTo(ExampleService.class); - assertThat(getDefinition(1).getClassToMock()) - .isEqualTo(ExampleServiceCaller.class); - } - - @Test - public void parseInferClassToMock() throws Exception { - this.parser.parse(InferClassToMock.class); - assertThat(getDefinitions()).hasSize(1); - assertThat(getDefinition(0).getClassToMock()).isEqualTo(ExampleService.class); - } - - @Test - public void parseMissingClassToMock() throws Exception { - this.thrown.expect(IllegalStateException.class); - this.thrown.expectMessage("Unable to deduce class to mock"); - this.parser.parse(MissingClassToMock.class); - } - - @Test - public void parseMultipleClasses() throws Exception { - this.parser.parse(MultipleClasses.class); - assertThat(getDefinitions()).hasSize(2); - assertThat(getDefinition(0).getClassToMock()).isEqualTo(ExampleService.class); - assertThat(getDefinition(1).getClassToMock()) - .isEqualTo(ExampleServiceCaller.class); - } - - @Test - public void parseMultipleClassesWithName() throws Exception { - this.thrown.expect(IllegalStateException.class); - this.thrown.expectMessage( - "The name attribute can only be used when mocking a single class"); - this.parser.parse(MultipleClassesWithName.class); - } - - private MockDefinition getDefinition(int index) { - return getDefinitions().get(index); - } - - private List getDefinitions() { - return new ArrayList(this.parser.getDefinitions()); - } - - @MockBean(ExampleService.class) - static class SingleMockBean { - - } - - @MockBeans({ @MockBean(ExampleService.class), @MockBean(ExampleServiceCaller.class) }) - static class RepeatMockBean { - - } - - @MyMockBean(ExampleService.class) - static class MetaMockBean { - - } - - @MockBean(name = "Name", classes = ExampleService.class, extraInterfaces = ExampleExtraInterface.class, answer = Answers.RETURNS_SMART_NULLS, serializable = true, reset = MockReset.NONE) - static class MockBeanAttributes { - - } - - @MockBean(ExampleService.class) - static class OnClassAndField { - - @MockBean(ExampleServiceCaller.class) - private Object caller; - - } - - @MockBean({ ExampleService.class, ExampleServiceCaller.class }) - static class MultipleClasses { - - } - - @MockBean(name = "name", classes = { ExampleService.class, - ExampleServiceCaller.class }) - static class MultipleClassesWithName { - - } - - static class InferClassToMock { - - @MockBean - private ExampleService exampleService; - - } - - @MockBean - static class MissingClassToMock { - - } - -} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoInitializeTestExecutionListenerTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoInitializeTestExecutionListenerTests.java index 27005a67014..b6cbb5c8577 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoInitializeTestExecutionListenerTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoInitializeTestExecutionListenerTests.java @@ -37,13 +37,13 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; /** - * Tests for {@link MockitoInitializeTestExecutionListener}. + * Tests for {@link MockitoTestExecutionListener}. * * @author Phillip Webb */ public class MockitoInitializeTestExecutionListenerTests { - private MockitoInitializeTestExecutionListener listener = new MockitoInitializeTestExecutionListener(); + private MockitoTestExecutionListener listener = new MockitoTestExecutionListener(); @Mock private ApplicationContext applicationContext; 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 9c6ca14c5c4..7ad46cf81fc 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 @@ -27,8 +27,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** - * Test for {@link MockitoPostProcessor}. See also the integration tests in the - * {@code runner} package. + * Test for {@link MockitoPostProcessor}. See also the integration tests. * * @author Phillip Webb */ diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnConfigurationClassForExistingBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnConfigurationClassForExistingBeanIntegrationTests.java new file mode 100644 index 00000000000..d4978ce8e57 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnConfigurationClassForExistingBeanIntegrationTests.java @@ -0,0 +1,56 @@ +/* + * 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.ExampleServiceCaller; +import org.springframework.boot.test.mock.mockito.example.SimpleExampleService; +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.Mockito.verify; + +/** + * Test {@link SpyBean} on a configuration class can be used to spy existing beans. + * + * @author Phillip Webb + */ +@RunWith(SpringRunner.class) +public class SpyBeanOnConfigurationClassForExistingBeanIntegrationTests { + + @Autowired + private ExampleServiceCaller caller; + + @Test + public void testSpying() throws Exception { + assertThat(this.caller.sayGreeting()).isEqualTo("I say simple"); + verify(this.caller.getService()).greeting(); + } + + @Configuration + @SpyBean(SimpleExampleService.class) + @Import({ ExampleServiceCaller.class, SimpleExampleService.class }) + static class Config { + + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnConfigurationClassForNewBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnConfigurationClassForNewBeanIntegrationTests.java new file mode 100644 index 00000000000..244a4d6f33c --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnConfigurationClassForNewBeanIntegrationTests.java @@ -0,0 +1,56 @@ +/* + * 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.ExampleServiceCaller; +import org.springframework.boot.test.mock.mockito.example.SimpleExampleService; +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.Mockito.verify; + +/** + * Test {@link SpyBean} on a configuration class can be used to inject new spy instances. + * + * @author Phillip Webb + */ +@RunWith(SpringRunner.class) +public class SpyBeanOnConfigurationClassForNewBeanIntegrationTests { + + @Autowired + private ExampleServiceCaller caller; + + @Test + public void testSpying() throws Exception { + assertThat(this.caller.sayGreeting()).isEqualTo("I say simple"); + verify(this.caller.getService()).greeting(); + } + + @Configuration + @SpyBean(SimpleExampleService.class) + @Import(ExampleServiceCaller.class) + static class Config { + + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnConfigurationFieldForExistingBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnConfigurationFieldForExistingBeanIntegrationTests.java new file mode 100644 index 00000000000..b326a65204a --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnConfigurationFieldForExistingBeanIntegrationTests.java @@ -0,0 +1,63 @@ +/* + * 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.ExampleService; +import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller; +import org.springframework.boot.test.mock.mockito.example.SimpleExampleService; +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.Mockito.verify; + +/** + * Test {@link SpyBean} on a field on a {@code @Configuration} class can be used to + * replace existing beans. + * + * @author Phillip Webb + */ +@RunWith(SpringRunner.class) +public class SpyBeanOnConfigurationFieldForExistingBeanIntegrationTests { + + @Autowired + private Config config; + + @Autowired + private ExampleServiceCaller caller; + + @Test + public void testSpying() throws Exception { + assertThat(this.caller.sayGreeting()).isEqualTo("I say simple"); + verify(this.config.exampleService).greeting(); + } + + @Configuration + @Import({ ExampleServiceCaller.class, SimpleExampleService.class }) + static class Config { + + @SpyBean + private ExampleService exampleService; + + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnConfigurationFieldForNewBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnConfigurationFieldForNewBeanIntegrationTests.java new file mode 100644 index 00000000000..4498753f0cf --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnConfigurationFieldForNewBeanIntegrationTests.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.ExampleServiceCaller; +import org.springframework.boot.test.mock.mockito.example.SimpleExampleService; +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.Mockito.verify; + +/** + * Test {@link SpyBean} on a field on a {@code @Configuration} class can be used to inject + * new spy instances. + * + * @author Phillip Webb + */ +@RunWith(SpringRunner.class) +public class SpyBeanOnConfigurationFieldForNewBeanIntegrationTests { + + @Autowired + private Config config; + + @Autowired + private ExampleServiceCaller caller; + + @Test + public void testSpying() throws Exception { + assertThat(this.caller.sayGreeting()).isEqualTo("I say simple"); + verify(this.config.exampleService).greeting(); + } + + @Configuration + @Import(ExampleServiceCaller.class) + static class Config { + + @SpyBean + private SimpleExampleService exampleService; + + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnContextHierarchyIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnContextHierarchyIntegrationTests.java new file mode 100644 index 00000000000..74b1a9c96e5 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnContextHierarchyIntegrationTests.java @@ -0,0 +1,88 @@ +/* + * 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.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.SpyBeanOnContextHierarchyIntegrationTests.ChildConfig; +import org.springframework.boot.test.mock.mockito.SpyBeanOnContextHierarchyIntegrationTests.ParentConfig; +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.SimpleExampleService; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.ContextHierarchy; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test {@link SpyBean} can be used with a {@link ContextHierarchy}. + * + * @author Phillip Webb + */ +@RunWith(SpringRunner.class) +@ContextHierarchy({ @ContextConfiguration(classes = ParentConfig.class), + @ContextConfiguration(classes = ChildConfig.class) }) +public class SpyBeanOnContextHierarchyIntegrationTests { + + @Autowired + private ChildConfig childConfig; + + @Test + public void testSpying() throws Exception { + ApplicationContext context = this.childConfig.getContext(); + ApplicationContext parentContext = context.getParent(); + assertThat(parentContext.getBeanNamesForType(ExampleService.class)).hasSize(1); + assertThat(parentContext.getBeanNamesForType(ExampleServiceCaller.class)) + .hasSize(0); + assertThat(context.getBeanNamesForType(ExampleService.class)).hasSize(0); + assertThat(context.getBeanNamesForType(ExampleServiceCaller.class)).hasSize(1); + assertThat(context.getBean(ExampleService.class)).isNotNull(); + assertThat(context.getBean(ExampleServiceCaller.class)).isNotNull(); + } + + @Configuration + @SpyBean(SimpleExampleService.class) + static class ParentConfig { + + } + + @Configuration + @SpyBean(ExampleServiceCaller.class) + static class ChildConfig implements ApplicationContextAware { + + private ApplicationContext context; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.context = applicationContext; + } + + public ApplicationContext getContext() { + return this.context; + } + + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnTestClassForExistingBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnTestClassForExistingBeanIntegrationTests.java new file mode 100644 index 00000000000..52f7924dee0 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnTestClassForExistingBeanIntegrationTests.java @@ -0,0 +1,56 @@ +/* + * 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.ExampleServiceCaller; +import org.springframework.boot.test.mock.mockito.example.SimpleExampleService; +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.Mockito.verify; + +/** + * Test {@link SpyBean} on a test class can be used to replace existing beans. + * + * @author Phillip Webb + */ +@RunWith(SpringRunner.class) +@SpyBean(SimpleExampleService.class) +public class SpyBeanOnTestClassForExistingBeanIntegrationTests { + + @Autowired + private ExampleServiceCaller caller; + + @Test + public void testSpying() throws Exception { + assertThat(this.caller.sayGreeting()).isEqualTo("I say simple"); + verify(this.caller.getService()).greeting(); + } + + @Configuration + @Import({ ExampleServiceCaller.class, SimpleExampleService.class }) + static class Config { + + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnTestClassForNewBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnTestClassForNewBeanIntegrationTests.java new file mode 100644 index 00000000000..cc5b193eb05 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnTestClassForNewBeanIntegrationTests.java @@ -0,0 +1,56 @@ +/* + * 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.ExampleServiceCaller; +import org.springframework.boot.test.mock.mockito.example.SimpleExampleService; +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.Mockito.verify; + +/** + * Test {@link SpyBean} on a test class can be used to inject new mock instances. + * + * @author Phillip Webb + */ +@RunWith(SpringRunner.class) +@SpyBean(SimpleExampleService.class) +public class SpyBeanOnTestClassForNewBeanIntegrationTests { + + @Autowired + private ExampleServiceCaller caller; + + @Test + public void testSpying() throws Exception { + assertThat(this.caller.sayGreeting()).isEqualTo("I say simple"); + verify(this.caller.getService()).greeting(); + } + + @Configuration + @Import(ExampleServiceCaller.class) + static class Config { + + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnTestFieldForExistingBeanCacheIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnTestFieldForExistingBeanCacheIntegrationTests.java new file mode 100644 index 00000000000..b955605584f --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnTestFieldForExistingBeanCacheIntegrationTests.java @@ -0,0 +1,56 @@ +/* + * 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.ExampleServiceCaller; +import org.springframework.boot.test.mock.mockito.example.SimpleExampleService; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.verify; + +/** + * Test {@link SpyBean} on a test class field can be used to replace existing beans when + * the context is cached. This test is identical to + * {@link SpyBeanOnTestFieldForExistingBeanIntegrationTests} so one of them should trigger + * application context caching. + * + * @author Phillip Webb + * @see SpyBeanOnTestFieldForExistingBeanIntegrationTests + */ +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = SpyBeanOnTestFieldForExistingBeanConfig.class) +public class SpyBeanOnTestFieldForExistingBeanCacheIntegrationTests { + + @SpyBean + private SimpleExampleService exampleService; + + @Autowired + private ExampleServiceCaller caller; + + @Test + public void testSpying() throws Exception { + assertThat(this.caller.sayGreeting()).isEqualTo("I say simple"); + verify(this.caller.getService()).greeting(); + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnTestFieldForExistingBeanConfig.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnTestFieldForExistingBeanConfig.java new file mode 100644 index 00000000000..016a3a4dac6 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnTestFieldForExistingBeanConfig.java @@ -0,0 +1,34 @@ +/* + * 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.springframework.boot.test.mock.mockito.example.ExampleServiceCaller; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * Config for {@link SpyBeanOnTestFieldForExistingBeanIntegrationTests} and + * {@link SpyBeanOnTestFieldForExistingBeanCacheIntegrationTests}. Extracted to a shared + * config to trigger caching. + * + * @author Phillip Webb + */ +@Configuration +@Import(ExampleServiceCaller.class) +public class SpyBeanOnTestFieldForExistingBeanConfig { + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnTestFieldForExistingBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnTestFieldForExistingBeanIntegrationTests.java new file mode 100644 index 00000000000..c972349eddd --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnTestFieldForExistingBeanIntegrationTests.java @@ -0,0 +1,53 @@ +/* + * 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.ExampleServiceCaller; +import org.springframework.boot.test.mock.mockito.example.SimpleExampleService; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.verify; + +/** + * Test {@link SpyBean} on a test class field can be used to replace existing beans. + * + * @author Phillip Webb + * @see SpyBeanOnTestFieldForExistingBeanCacheIntegrationTests + */ +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = MockBeanOnTestFieldForExistingBeanConfig.class) +public class SpyBeanOnTestFieldForExistingBeanIntegrationTests { + + @SpyBean + private SimpleExampleService exampleService; + + @Autowired + private ExampleServiceCaller caller; + + @Test + public void testSpying() throws Exception { + assertThat(this.caller.sayGreeting()).isEqualTo("I say simple"); + verify(this.caller.getService()).greeting(); + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnTestFieldForNewBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnTestFieldForNewBeanIntegrationTests.java new file mode 100644 index 00000000000..ebad7dd35e0 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyBeanOnTestFieldForNewBeanIntegrationTests.java @@ -0,0 +1,59 @@ +/* + * 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.ExampleService; +import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller; +import org.springframework.boot.test.mock.mockito.example.SimpleExampleService; +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.Mockito.verify; + +/** + * Test {@link SpyBean} on a test class field can be used to inject new mock instances. + * + * @author Phillip Webb + */ +@RunWith(SpringRunner.class) +public class SpyBeanOnTestFieldForNewBeanIntegrationTests { + + @SpyBean + private ExampleService exampleService; + + @Autowired + private ExampleServiceCaller caller; + + @Test + public void testSpying() throws Exception { + assertThat(this.caller.sayGreeting()).isEqualTo("I say simple"); + verify(this.caller.getService()).greeting(); + } + + @Configuration + @Import({ ExampleServiceCaller.class, SimpleExampleService.class }) + static class Config { + + } + +} 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 new file mode 100644 index 00000000000..71b99f2d057 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyDefinitionTests.java @@ -0,0 +1,107 @@ +/* + * 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.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Answers; +import org.mockito.internal.util.MockUtil; +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 static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link SpyDefinition}. + * + * @author Phillip Webb + */ +public class SpyDefinitionTests { + + @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"); + new SpyDefinition(null, null, null); + } + + @Test + public void createWithDefaults() throws Exception { + SpyDefinition definition = new SpyDefinition(null, RealExampleService.class, + null); + assertThat(definition.getName()).isNull(); + assertThat(definition.getClassToSpy()).isEqualTo(RealExampleService.class); + assertThat(definition.getReset()).isEqualTo(MockReset.AFTER); + } + + @Test + public void createExplicit() throws Exception { + SpyDefinition definition = new SpyDefinition("name", RealExampleService.class, + MockReset.BEFORE); + assertThat(definition.getName()).isEqualTo("name"); + assertThat(definition.getClassToSpy()).isEqualTo(RealExampleService.class); + assertThat(definition.getReset()).isEqualTo(MockReset.BEFORE); + } + + @Test + public void createSpy() throws Exception { + SpyDefinition definition = new SpyDefinition("name", RealExampleService.class, + MockReset.BEFORE); + RealExampleService spy = definition.createSpy(new RealExampleService("hello")); + MockCreationSettings settings = new MockUtil().getMockSettings(spy); + assertThat(spy).isInstanceOf(ExampleService.class); + assertThat(settings.getMockName().toString()).isEqualTo("name"); + assertThat(settings.getDefaultAnswer()) + .isEqualTo(Answers.CALLS_REAL_METHODS.get()); + assertThat(MockReset.get(spy)).isEqualTo(MockReset.BEFORE); + } + + @Test + public void createSpyWhenNullInstanceShouldThrowException() throws Exception { + SpyDefinition definition = new SpyDefinition("name", RealExampleService.class, + MockReset.BEFORE); + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Instance must not be null"); + definition.createSpy(null); + } + + @Test + public void createSpyWhenWrongInstanceShouldThrowException() throws Exception { + SpyDefinition definition = new SpyDefinition("name", RealExampleService.class, + MockReset.BEFORE); + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("must be an instance of"); + definition.createSpy(new ExampleServiceCaller(null)); + } + + @Test + public void createSpyTwice() throws Exception { + SpyDefinition definition = new SpyDefinition("name", RealExampleService.class, + MockReset.BEFORE); + Object instance = new RealExampleService("hello"); + instance = definition.createSpy(instance); + instance = definition.createSpy(instance); + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/RealExampleService.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/RealExampleService.java new file mode 100644 index 00000000000..f241fe25835 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/RealExampleService.java @@ -0,0 +1,37 @@ +/* + * 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 implementation for spy tests. + * + * @author Phillip Webb + */ +public class RealExampleService implements ExampleService { + + private final String greeting; + + public RealExampleService(String greeting) { + this.greeting = greeting; + } + + @Override + public String greeting() { + return this.greeting; + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/SimpleExampleService.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/SimpleExampleService.java new file mode 100644 index 00000000000..143213ada64 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/SimpleExampleService.java @@ -0,0 +1,30 @@ +/* + * 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 implementation for spy tests. + * + * @author Phillip Webb + */ +public class SimpleExampleService extends RealExampleService { + + public SimpleExampleService() { + super("simple"); + } + +}