Commit 34070e5a authored by Phillip Webb's avatar Phillip Webb

Add Support for Mockito spies

Add a @SpyBean annotation that can be used to create spies.

Fixes gh-5538
parent 1d4f38e4
/*
* 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;
}
}
...@@ -33,24 +33,25 @@ import org.springframework.util.ReflectionUtils.FieldCallback; ...@@ -33,24 +33,25 @@ import org.springframework.util.ReflectionUtils.FieldCallback;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
* Parser to create {@link MockDefinition} from {@link MockBean @MockBean} annotations * Parser to create {@link MockDefinition} and {@link SpyDefinition} instances from
* declared on or in a class. * {@link MockBean @MockBean} and {@link SpyBean @SpyBean} annotations declared on or in a
* class.
* *
* @author Phillip Webb * @author Phillip Webb
*/ */
class MockDefinitionsParser { class DefinitionsParser {
private final Set<MockDefinition> definitions; private final Set<Definition> definitions;
private final Map<MockDefinition, Field> fields; private final Map<Definition, Field> definitionFields;
MockDefinitionsParser() { DefinitionsParser() {
this(Collections.<MockDefinition>emptySet()); this(Collections.<Definition>emptySet());
} }
MockDefinitionsParser(Collection<? extends MockDefinition> existing) { DefinitionsParser(Collection<? extends Definition> existing) {
this.definitions = new LinkedHashSet<MockDefinition>(); this.definitions = new LinkedHashSet<Definition>();
this.fields = new LinkedHashMap<MockDefinition, Field>(); this.definitionFields = new LinkedHashMap<Definition, Field>();
if (existing != null) { if (existing != null) {
this.definitions.addAll(existing); this.definitions.addAll(existing);
} }
...@@ -72,12 +73,16 @@ class MockDefinitionsParser { ...@@ -72,12 +73,16 @@ class MockDefinitionsParser {
private void parseElement(AnnotatedElement element) { private void parseElement(AnnotatedElement element) {
for (MockBean annotation : AnnotationUtils.getRepeatableAnnotations(element, for (MockBean annotation : AnnotationUtils.getRepeatableAnnotations(element,
MockBean.class, MockBeans.class)) { 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) { private void parseMockBeanAnnotation(MockBean annotation, AnnotatedElement element) {
Set<Class<?>> classesToMock = getOrDeduceClassesToMock(annotation, element); Set<Class<?>> classesToMock = getOrDeduceClasses(element, annotation.value());
Assert.state(!classesToMock.isEmpty(), Assert.state(!classesToMock.isEmpty(),
"Unable to deduce class to mock from " + element); "Unable to deduce class to mock from " + element);
if (StringUtils.hasLength(annotation.name())) { if (StringUtils.hasLength(annotation.name())) {
...@@ -88,30 +93,50 @@ class MockDefinitionsParser { ...@@ -88,30 +93,50 @@ class MockDefinitionsParser {
MockDefinition definition = new MockDefinition(annotation.name(), classToMock, MockDefinition definition = new MockDefinition(annotation.name(), classToMock,
annotation.extraInterfaces(), annotation.answer(), annotation.extraInterfaces(), annotation.answer(),
annotation.serializable(), annotation.reset()); annotation.serializable(), annotation.reset());
boolean isNewDefinition = this.definitions.add(definition); addDefinition(element, definition, "mock");
Assert.state(isNewDefinition, "Duplicate mock definition " + definition); }
if (element instanceof Field) { }
this.fields.put(definition, (Field) element);
} private void parseSpyBeanAnnotation(SpyBean annotation, AnnotatedElement element) {
Set<Class<?>> 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<Class<?>> getOrDeduceClassesToMock(MockBean annotation, private Set<Class<?>> getOrDeduceClasses(AnnotatedElement element, Class<?>[] value) {
AnnotatedElement element) {
Set<Class<?>> classes = new LinkedHashSet<Class<?>>(); Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
classes.addAll(Arrays.asList(annotation.value())); classes.addAll(Arrays.asList(value));
if (classes.isEmpty() && element instanceof Field) { if (classes.isEmpty() && element instanceof Field) {
classes.add(((Field) element).getType()); classes.add(((Field) element).getType());
} }
return classes; return classes;
} }
public Set<MockDefinition> getDefinitions() { public Set<Definition> getDefinitions() {
return Collections.unmodifiableSet(this.definitions); return Collections.unmodifiableSet(this.definitions);
} }
public Field getField(MockDefinition definition) { public Field getField(Definition definition) {
return this.fields.get(definition); return this.definitionFields.get(definition);
} }
} }
...@@ -82,9 +82,9 @@ import org.springframework.test.context.junit4.SpringRunner; ...@@ -82,9 +82,9 @@ import org.springframework.test.context.junit4.SpringRunner;
public @interface MockBean { public @interface MockBean {
/** /**
* The name of the bean that should be registered with the application context. If not * The name of the bean to register or replace. If not specified the name will either
* specified the name will either be generated or, if the mock replaces an existing * be generated or, if the mock replaces an existing bean, the existing name will be
* bean, the existing name will be used. * used.
* @return the name of the bean * @return the name of the bean
*/ */
String name() default ""; String name() default "";
......
...@@ -35,12 +35,10 @@ import org.springframework.util.StringUtils; ...@@ -35,12 +35,10 @@ import org.springframework.util.StringUtils;
* *
* @author Phillip Webb * @author Phillip Webb
*/ */
class MockDefinition { class MockDefinition extends Definition {
private static final int MULTIPLIER = 31; private static final int MULTIPLIER = 31;
private final String name;
private final Class<?> classToMock; private final Class<?> classToMock;
private final Set<Class<?>> extraInterfaces; private final Set<Class<?>> extraInterfaces;
...@@ -49,21 +47,18 @@ class MockDefinition { ...@@ -49,21 +47,18 @@ class MockDefinition {
private final boolean serializable; private final boolean serializable;
private final MockReset reset;
MockDefinition(Class<?> classToMock) { MockDefinition(Class<?> classToMock) {
this(null, classToMock, null, null, false, null); this(null, classToMock, null, null, false, null);
} }
MockDefinition(String name, Class<?> classToMock, Class<?>[] extraInterfaces, MockDefinition(String name, Class<?> classToMock, Class<?>[] extraInterfaces,
Answers answer, boolean serializable, MockReset reset) { Answers answer, boolean serializable, MockReset reset) {
super(name, reset);
Assert.notNull(classToMock, "ClassToMock must not be null"); Assert.notNull(classToMock, "ClassToMock must not be null");
this.name = name;
this.classToMock = classToMock; this.classToMock = classToMock;
this.extraInterfaces = asClassSet(extraInterfaces); this.extraInterfaces = asClassSet(extraInterfaces);
this.answer = (answer != null ? answer : Answers.RETURNS_DEFAULTS); this.answer = (answer != null ? answer : Answers.RETURNS_DEFAULTS);
this.serializable = serializable; this.serializable = serializable;
this.reset = (reset != null ? reset : MockReset.AFTER);
} }
private Set<Class<?>> asClassSet(Class<?>[] classes) { private Set<Class<?>> asClassSet(Class<?>[] classes) {
...@@ -74,14 +69,6 @@ class MockDefinition { ...@@ -74,14 +69,6 @@ class MockDefinition {
return Collections.unmodifiableSet(classSet); 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 classes that should be mocked.
* @return the class to mock; never {@code null} * @return the class to mock; never {@code null}
...@@ -114,23 +101,13 @@ class MockDefinition { ...@@ -114,23 +101,13 @@ class MockDefinition {
return this.serializable; return this.serializable;
} }
/**
* Return the mock reset mode.
* @return the reset mode
*/
public MockReset getReset() {
return this.reset;
}
@Override @Override
public int hashCode() { public int hashCode() {
int result = 1; int result = super.hashCode();
result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.name);
result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.classToMock); result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.classToMock);
result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.extraInterfaces); result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.extraInterfaces);
result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.answer); result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.answer);
result = MULTIPLIER * result + (this.serializable ? 1231 : 1237); result = MULTIPLIER * result + (this.serializable ? 1231 : 1237);
result = MULTIPLIER * result + ObjectUtils.nullSafeHashCode(this.reset);
return result; return result;
} }
...@@ -143,32 +120,30 @@ class MockDefinition { ...@@ -143,32 +120,30 @@ class MockDefinition {
return false; return false;
} }
MockDefinition other = (MockDefinition) obj; MockDefinition other = (MockDefinition) obj;
boolean result = true; boolean result = super.equals(obj);
result &= ObjectUtils.nullSafeEquals(this.name, other.name);
result &= ObjectUtils.nullSafeEquals(this.classToMock, other.classToMock); result &= ObjectUtils.nullSafeEquals(this.classToMock, other.classToMock);
result &= ObjectUtils.nullSafeEquals(this.extraInterfaces, other.extraInterfaces); result &= ObjectUtils.nullSafeEquals(this.extraInterfaces, other.extraInterfaces);
result &= ObjectUtils.nullSafeEquals(this.answer, other.answer); result &= ObjectUtils.nullSafeEquals(this.answer, other.answer);
result &= this.serializable == other.serializable; result &= this.serializable == other.serializable;
result &= ObjectUtils.nullSafeEquals(this.reset, other.reset);
return result; return result;
} }
@Override @Override
public String toString() { public String toString() {
return new ToStringCreator(this).append("name", this.name) return new ToStringCreator(this).append("name", getName())
.append("classToMock", this.classToMock) .append("classToMock", this.classToMock)
.append("extraInterfaces", this.extraInterfaces) .append("extraInterfaces", this.extraInterfaces)
.append("answer", this.answer).append("serializable", this.serializable) .append("answer", this.answer).append("serializable", this.serializable)
.append("reset", this.reset).toString(); .append("reset", getReset()).toString();
} }
public <T> T createMock() { public <T> T createMock() {
return createMock(this.name); return createMock(getName());
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T createMock(String name) { public <T> T createMock(String name) {
MockSettings settings = MockReset.withSettings(this.reset); MockSettings settings = MockReset.withSettings(getReset());
if (StringUtils.hasLength(name)) { if (StringUtils.hasLength(name)) {
settings.name(name); settings.name(name);
} }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package org.springframework.boot.test.mock.mockito; package org.springframework.boot.test.mock.mockito;
import java.util.LinkedHashSet;
import java.util.Set; import java.util.Set;
import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistry;
...@@ -30,10 +31,10 @@ import org.springframework.test.context.MergedContextConfiguration; ...@@ -30,10 +31,10 @@ import org.springframework.test.context.MergedContextConfiguration;
*/ */
class MockitoContextCustomizer implements ContextCustomizer { class MockitoContextCustomizer implements ContextCustomizer {
private final Set<MockDefinition> definitions; private final Set<Definition> definitions;
MockitoContextCustomizer(Set<MockDefinition> definitions) { MockitoContextCustomizer(Set<? extends Definition> definitions) {
this.definitions = definitions; this.definitions = new LinkedHashSet<Definition>(definitions);
} }
@Override @Override
......
...@@ -34,7 +34,7 @@ class MockitoContextCustomizerFactory implements ContextCustomizerFactory { ...@@ -34,7 +34,7 @@ class MockitoContextCustomizerFactory implements ContextCustomizerFactory {
List<ContextConfigurationAttributes> configAttributes) { List<ContextConfigurationAttributes> configAttributes) {
// We gather the explicit mock definitions here since they form part of the // We gather the explicit mock definitions here since they form part of the
// MergedContextConfiguration key. Different mocks need to have a different key // MergedContextConfiguration key. Different mocks need to have a different key
MockDefinitionsParser parser = new MockDefinitionsParser(); DefinitionsParser parser = new DefinitionsParser();
parser.parse(testClass); parser.parse(testClass);
return new MockitoContextCustomizer(parser.getDefinitions()); return new MockitoContextCustomizer(parser.getDefinitions());
} }
......
...@@ -34,6 +34,7 @@ import org.springframework.beans.factory.BeanFactory; ...@@ -34,6 +34,7 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor; 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.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
...@@ -66,7 +67,7 @@ import org.springframework.util.StringUtils; ...@@ -66,7 +67,7 @@ import org.springframework.util.StringUtils;
*/ */
public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAdapter public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
implements BeanClassLoaderAware, BeanFactoryAware, BeanFactoryPostProcessor, implements BeanClassLoaderAware, BeanFactoryAware, BeanFactoryPostProcessor,
Ordered { BeanPostProcessor, Ordered {
private static final String BEAN_NAME = MockitoPostProcessor.class.getName(); private static final String BEAN_NAME = MockitoPostProcessor.class.getName();
...@@ -74,7 +75,7 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda ...@@ -74,7 +75,7 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, .getQualifiedAttributeName(ConfigurationClassPostProcessor.class,
"configurationClass"); "configurationClass");
private final Set<MockDefinition> mockDefinitions; private final Set<Definition> definitions;
private ClassLoader classLoader; private ClassLoader classLoader;
...@@ -82,17 +83,19 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda ...@@ -82,17 +83,19 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
private final BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator(); private final BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator();
private Map<MockDefinition, String> beanNameRegistry = new HashMap<MockDefinition, String>(); private Map<Definition, String> beanNameRegistry = new HashMap<Definition, String>();
private Map<Field, String> fieldRegistry = new HashMap<Field, String>(); private Map<Field, String> fieldRegistry = new HashMap<Field, String>();
private Map<String, SpyDefinition> spies = new HashMap<String, SpyDefinition>();
/** /**
* Create a new {@link MockitoPostProcessor} instance with the given initial * Create a new {@link MockitoPostProcessor} instance with the given initial
* definitions. * definitions.
* @param mockDefinitions the initial definitions * @param definitions the initial definitions
*/ */
public MockitoPostProcessor(Set<MockDefinition> mockDefinitions) { public MockitoPostProcessor(Set<Definition> definitions) {
this.mockDefinitions = mockDefinitions; this.definitions = definitions;
} }
@Override @Override
...@@ -118,14 +121,14 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda ...@@ -118,14 +121,14 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
private void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory, private void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory,
BeanDefinitionRegistry registry) { BeanDefinitionRegistry registry) {
MockDefinitionsParser parser = new MockDefinitionsParser(this.mockDefinitions); DefinitionsParser parser = new DefinitionsParser(this.definitions);
for (Class<?> configurationClass : getConfigurationClasses(beanFactory)) { for (Class<?> configurationClass : getConfigurationClasses(beanFactory)) {
parser.parse(configurationClass); parser.parse(configurationClass);
} }
Set<MockDefinition> definitions = parser.getDefinitions(); Set<Definition> definitions = parser.getDefinitions();
for (MockDefinition definition : definitions) { for (Definition definition : definitions) {
Field field = parser.getField(definition); Field field = parser.getField(definition);
registerMock(beanFactory, registry, definition, field); register(beanFactory, registry, definition, field);
} }
} }
...@@ -152,20 +155,23 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda ...@@ -152,20 +155,23 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
return definitions; return definitions;
} }
void inject(Field field, Object target, MockDefinition definition) { private void register(ConfigurableListableBeanFactory beanFactory,
String beanName = this.beanNameRegistry.get(definition); BeanDefinitionRegistry registry, Definition definition, Field field) {
Assert.state(StringUtils.hasLength(beanName), if (definition instanceof MockDefinition) {
"No mock found for definition " + definition); registerMock(beanFactory, registry, (MockDefinition) definition, field);
injectMock(field, target, beanName); }
else if (definition instanceof SpyDefinition) {
registerSpy(beanFactory, registry, (SpyDefinition) definition, field);
}
} }
private void registerMock(ConfigurableListableBeanFactory beanFactory, private void registerMock(ConfigurableListableBeanFactory beanFactory,
BeanDefinitionRegistry registry, MockDefinition mockDefinition, Field field) { BeanDefinitionRegistry registry, MockDefinition definition, Field field) {
RootBeanDefinition beanDefinition = createBeanDefinition(mockDefinition); RootBeanDefinition beanDefinition = createBeanDefinition(definition);
String name = getBeanName(beanFactory, registry, mockDefinition, beanDefinition); String name = getBeanName(beanFactory, registry, definition, beanDefinition);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(1, name); beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(1, name);
registry.registerBeanDefinition(name, beanDefinition); registry.registerBeanDefinition(name, beanDefinition);
this.beanNameRegistry.put(mockDefinition, name); this.beanNameRegistry.put(definition, name);
if (field != null) { if (field != null) {
this.fieldRegistry.put(field, name); this.fieldRegistry.put(field, name);
} }
...@@ -184,12 +190,12 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda ...@@ -184,12 +190,12 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
/** /**
* Factory method used by defined beans to actually create the mock. * 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 * @param name the bean name
* @return the mock instance * @return the mock instance
*/ */
protected final Object createMock(MockDefinition definition, String name) { protected final Object createMock(MockDefinition mockDefinition, String name) {
return definition.createMock(name + " bean"); return mockDefinition.createMock(name + " bean");
} }
private String getBeanName(ConfigurableListableBeanFactory beanFactory, private String getBeanName(ConfigurableListableBeanFactory beanFactory,
...@@ -212,6 +218,60 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda ...@@ -212,6 +218,60 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
+ new TreeSet<String>(Arrays.asList(existingBeans))); + new TreeSet<String>(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<String>(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 @Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, public PropertyValues postProcessPropertyValues(PropertyValues pvs,
PropertyDescriptor[] pds, final Object bean, String beanName) PropertyDescriptor[] pds, final Object bean, String beanName)
...@@ -231,18 +291,27 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda ...@@ -231,18 +291,27 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
private void postProcessField(Object bean, Field field) { private void postProcessField(Object bean, Field field) {
String beanName = this.fieldRegistry.get(field); String beanName = this.fieldRegistry.get(field);
if (StringUtils.hasLength(beanName)) { 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 { try {
field.setAccessible(true); field.setAccessible(true);
Object mockBean = this.beanFactory.getBean(beanName, field.getType()); Assert.state(ReflectionUtils.getField(field, target) == null,
ReflectionUtils.setField(field, target, mockBean); "The field " + field + " cannot have an existing value");
Object bean = this.beanFactory.getBean(beanName, field.getType());
ReflectionUtils.setField(field, target, bean);
} }
catch (Throwable ex) { 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 ...@@ -264,11 +333,11 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
* Register the processor with a {@link BeanDefinitionRegistry}. Not required when * Register the processor with a {@link BeanDefinitionRegistry}. Not required when
* using the {@link SpringRunner} as registration is automatic. * using the {@link SpringRunner} as registration is automatic.
* @param registry the bean definition registry * @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, public static void register(BeanDefinitionRegistry registry,
Set<MockDefinition> mockDefinitions) { Set<Definition> definitions) {
register(registry, MockitoPostProcessor.class, mockDefinitions); register(registry, MockitoPostProcessor.class, definitions);
} }
/** /**
...@@ -276,18 +345,18 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda ...@@ -276,18 +345,18 @@ public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAda
* using the {@link SpringRunner} as registration is automatic. * using the {@link SpringRunner} as registration is automatic.
* @param registry the bean definition registry * @param registry the bean definition registry
* @param postProcessor the post processor class to register * @param postProcessor the post processor class to register
* @param mockDefinitions the initial mock definitions * @param definitions the initial mock/spy definitions
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static void register(BeanDefinitionRegistry registry, public static void register(BeanDefinitionRegistry registry,
Class<? extends MockitoPostProcessor> postProcessor, Class<? extends MockitoPostProcessor> postProcessor,
Set<MockDefinition> mockDefinitions) { Set<Definition> definitions) {
BeanDefinition definition = getOrAddBeanDefinition(registry, postProcessor); BeanDefinition definition = getOrAddBeanDefinition(registry, postProcessor);
ValueHolder constructorArg = definition.getConstructorArgumentValues() ValueHolder constructorArg = definition.getConstructorArgumentValues()
.getIndexedArgumentValue(0, Set.class); .getIndexedArgumentValue(0, Set.class);
Set<MockDefinition> existing = (Set<MockDefinition>) constructorArg.getValue(); Set<Definition> existing = (Set<Definition>) constructorArg.getValue();
if (mockDefinitions != null) { if (definitions != null) {
existing.addAll(mockDefinitions); existing.addAll(definitions);
} }
} }
......
...@@ -38,10 +38,11 @@ import org.springframework.util.ReflectionUtils.FieldCallback; ...@@ -38,10 +38,11 @@ import org.springframework.util.ReflectionUtils.FieldCallback;
* *
* @author Phillip Webb * @author Phillip Webb
*/ */
class MockitoInitializeTestExecutionListener extends AbstractTestExecutionListener { class MockitoTestExecutionListener extends AbstractTestExecutionListener {
@Override @Override
public void prepareTestInstance(TestContext testContext) throws Exception { public void prepareTestInstance(TestContext testContext) throws Exception {
System.out.println("Prepare");
if (hasMockitoAnnotations(testContext)) { if (hasMockitoAnnotations(testContext)) {
MockitoAnnotations.initMocks(testContext.getTestInstance()); MockitoAnnotations.initMocks(testContext.getTestInstance());
} }
...@@ -55,18 +56,18 @@ class MockitoInitializeTestExecutionListener extends AbstractTestExecutionListen ...@@ -55,18 +56,18 @@ class MockitoInitializeTestExecutionListener extends AbstractTestExecutionListen
} }
private void injectFields(TestContext testContext) { private void injectFields(TestContext testContext) {
MockDefinitionsParser parser = new MockDefinitionsParser(); DefinitionsParser parser = new DefinitionsParser();
parser.parse(testContext.getTestClass()); parser.parse(testContext.getTestClass());
if (!parser.getDefinitions().isEmpty()) { if (!parser.getDefinitions().isEmpty()) {
injectFields(testContext, parser); injectFields(testContext, parser);
} }
} }
private void injectFields(TestContext testContext, MockDefinitionsParser parser) { private void injectFields(TestContext testContext, DefinitionsParser parser) {
ApplicationContext applicationContext = testContext.getApplicationContext(); ApplicationContext applicationContext = testContext.getApplicationContext();
MockitoPostProcessor postProcessor = applicationContext MockitoPostProcessor postProcessor = applicationContext
.getBean(MockitoPostProcessor.class); .getBean(MockitoPostProcessor.class);
for (MockDefinition definition : parser.getDefinitions()) { for (Definition definition : parser.getDefinitions()) {
Field field = parser.getField(definition); Field field = parser.getField(definition);
if (field != null) { if (field != null) {
postProcessor.inject(field, testContext.getTestInstance(), definition); postProcessor.inject(field, testContext.getTestInstance(), definition);
......
/*
* 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}.
* <p>
* 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.
* <p>
* 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: <pre class="code">
* &#064;RunWith(SpringRunner.class)
* public class ExampleTests {
*
* &#064;SpyBean
* private ExampleService service;
*
* &#064;Autowired
* private UserOfService userOfService;
*
* &#064;Test
* public void testUserOfService() {
* String actual = this.userOfService.makeUse();
* assertEquals("Was: Hello", actual);
* verify(this.service).greet();
* }
*
* &#064;Configuration
* &#064;Import(UserOfService.class) // A &#064;Component injected with ExampleService
* static class Config {
* }
*
*
* }
* </pre>
* <p>
* 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.
* <p>
* When {@code @MockBean} also defines a {@code name} this attribute can only contain
* a single value.
* <p>
* 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;
}
/*
* 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.
* <p>
* Can be used natively, declaring several nested {@link SpyBean} annotations. Can also be
* used in conjunction with Java 8's support for <em>repeatable annotations</em>, 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();
}
/*
* 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> T createSpy(Object instance) {
return createSpy(getName(), instance);
}
@SuppressWarnings("unchecked")
public <T> 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);
}
}
...@@ -7,5 +7,5 @@ org.springframework.boot.test.mock.mockito.MockitoContextCustomizerFactory ...@@ -7,5 +7,5 @@ org.springframework.boot.test.mock.mockito.MockitoContextCustomizerFactory
# Test Execution Listeners # Test Execution Listeners
org.springframework.test.context.TestExecutionListener=\ 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 org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener
...@@ -19,7 +19,6 @@ package org.springframework.boot.test.mock.mockito; ...@@ -19,7 +19,6 @@ package org.springframework.boot.test.mock.mockito;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.junit.Ignore;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
...@@ -28,51 +27,43 @@ import org.mockito.Answers; ...@@ -28,51 +27,43 @@ import org.mockito.Answers;
import org.springframework.boot.test.mock.mockito.example.ExampleExtraInterface; import org.springframework.boot.test.mock.mockito.example.ExampleExtraInterface;
import org.springframework.boot.test.mock.mockito.example.ExampleService; import org.springframework.boot.test.mock.mockito.example.ExampleService;
import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller; import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller;
import org.springframework.boot.test.mock.mockito.example.MyMockBean; import org.springframework.boot.test.mock.mockito.example.RealExampleService;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
/** /**
* Tests for {@link MockDefinitionsParser}. * Tests for {@link DefinitionsParser}.
* *
* @author Phillip Webb * @author Phillip Webb
*/ */
public class MockDefinitionsParserTests { public class DefinitionsParserTests {
@Rule @Rule
public ExpectedException thrown = ExpectedException.none(); public ExpectedException thrown = ExpectedException.none();
private MockDefinitionsParser parser = new MockDefinitionsParser(); private DefinitionsParser parser = new DefinitionsParser();
@Test @Test
public void parseSingleMockBean() { public void parseSingleMockBean() {
this.parser.parse(SingleMockBean.class); this.parser.parse(SingleMockBean.class);
assertThat(getDefinitions()).hasSize(1); assertThat(getDefinitions()).hasSize(1);
assertThat(getDefinition(0).getClassToMock()).isEqualTo(ExampleService.class); assertThat(getMockDefinition(0).getClassToMock()).isEqualTo(ExampleService.class);
} }
@Test @Test
public void parseRepeatMockBean() { public void parseRepeatMockBean() {
this.parser.parse(RepeatMockBean.class); this.parser.parse(RepeatMockBean.class);
assertThat(getDefinitions()).hasSize(2); assertThat(getDefinitions()).hasSize(2);
assertThat(getDefinition(0).getClassToMock()).isEqualTo(ExampleService.class); assertThat(getMockDefinition(0).getClassToMock()).isEqualTo(ExampleService.class);
assertThat(getDefinition(1).getClassToMock()) assertThat(getMockDefinition(1).getClassToMock())
.isEqualTo(ExampleServiceCaller.class); .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 @Test
public void parseMockBeanAttributes() throws Exception { public void parseMockBeanAttributes() throws Exception {
this.parser.parse(MockBeanAttributes.class); this.parser.parse(MockBeanAttributes.class);
assertThat(getDefinitions()).hasSize(1); assertThat(getDefinitions()).hasSize(1);
MockDefinition definition = getDefinition(0); MockDefinition definition = getMockDefinition(0);
assertThat(definition.getName()).isEqualTo("Name"); assertThat(definition.getName()).isEqualTo("Name");
assertThat(definition.getClassToMock()).isEqualTo(ExampleService.class); assertThat(definition.getClassToMock()).isEqualTo(ExampleService.class);
assertThat(definition.getExtraInterfaces()) assertThat(definition.getExtraInterfaces())
...@@ -83,51 +74,126 @@ public class MockDefinitionsParserTests { ...@@ -83,51 +74,126 @@ public class MockDefinitionsParserTests {
} }
@Test @Test
public void parseOnClassAndField() throws Exception { public void parseMockBeanOnClassAndField() throws Exception {
this.parser.parse(OnClassAndField.class); this.parser.parse(MockBeanOnClassAndField.class);
assertThat(getDefinitions()).hasSize(2); assertThat(getDefinitions()).hasSize(2);
assertThat(getDefinition(0).getClassToMock()).isEqualTo(ExampleService.class); assertThat(getMockDefinition(0).getClassToMock()).isEqualTo(ExampleService.class);
assertThat(getDefinition(1).getClassToMock()) assertThat(getMockDefinition(1).getClassToMock())
.isEqualTo(ExampleServiceCaller.class); .isEqualTo(ExampleServiceCaller.class);
} }
@Test @Test
public void parseInferClassToMock() throws Exception { public void parseMockBeanInferClassToMock() throws Exception {
this.parser.parse(InferClassToMock.class); this.parser.parse(MockBeanInferClassToMock.class);
assertThat(getDefinitions()).hasSize(1); assertThat(getDefinitions()).hasSize(1);
assertThat(getDefinition(0).getClassToMock()).isEqualTo(ExampleService.class); assertThat(getMockDefinition(0).getClassToMock()).isEqualTo(ExampleService.class);
} }
@Test @Test
public void parseMissingClassToMock() throws Exception { public void parseMockBeanMissingClassToMock() throws Exception {
this.thrown.expect(IllegalStateException.class); this.thrown.expect(IllegalStateException.class);
this.thrown.expectMessage("Unable to deduce class to mock"); this.thrown.expectMessage("Unable to deduce class to mock");
this.parser.parse(MissingClassToMock.class); this.parser.parse(MockBeanMissingClassToMock.class);
} }
@Test @Test
public void parseMultipleClasses() throws Exception { public void parseMockBeanMultipleClasses() throws Exception {
this.parser.parse(MultipleClasses.class); this.parser.parse(MockBeanMultipleClasses.class);
assertThat(getDefinitions()).hasSize(2); assertThat(getDefinitions()).hasSize(2);
assertThat(getDefinition(0).getClassToMock()).isEqualTo(ExampleService.class); assertThat(getMockDefinition(0).getClassToMock()).isEqualTo(ExampleService.class);
assertThat(getDefinition(1).getClassToMock()) assertThat(getMockDefinition(1).getClassToMock())
.isEqualTo(ExampleServiceCaller.class); .isEqualTo(ExampleServiceCaller.class);
} }
@Test @Test
public void parseMultipleClassesWithName() throws Exception { public void parseMockBeanMultipleClassesWithName() throws Exception {
this.thrown.expect(IllegalStateException.class); this.thrown.expect(IllegalStateException.class);
this.thrown.expectMessage( this.thrown.expectMessage(
"The name attribute can only be used when mocking a single class"); "The name attribute can only be used when mocking a single class");
this.parser.parse(MultipleClassesWithName.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);
} }
private MockDefinition getDefinition(int index) { @Test
return getDefinitions().get(index); 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);
} }
private List<MockDefinition> getDefinitions() { @Test
return new ArrayList<MockDefinition>(this.parser.getDefinitions()); 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<Definition> getDefinitions() {
return new ArrayList<Definition>(this.parser.getDefinitions());
} }
@MockBean(ExampleService.class) @MockBean(ExampleService.class)
...@@ -140,18 +206,13 @@ public class MockDefinitionsParserTests { ...@@ -140,18 +206,13 @@ public class MockDefinitionsParserTests {
} }
@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) @MockBean(name = "Name", classes = ExampleService.class, extraInterfaces = ExampleExtraInterface.class, answer = Answers.RETURNS_SMART_NULLS, serializable = true, reset = MockReset.NONE)
static class MockBeanAttributes { static class MockBeanAttributes {
} }
@MockBean(ExampleService.class) @MockBean(ExampleService.class)
static class OnClassAndField { static class MockBeanOnClassAndField {
@MockBean(ExampleServiceCaller.class) @MockBean(ExampleServiceCaller.class)
private Object caller; private Object caller;
...@@ -159,17 +220,17 @@ public class MockDefinitionsParserTests { ...@@ -159,17 +220,17 @@ public class MockDefinitionsParserTests {
} }
@MockBean({ ExampleService.class, ExampleServiceCaller.class }) @MockBean({ ExampleService.class, ExampleServiceCaller.class })
static class MultipleClasses { static class MockBeanMultipleClasses {
} }
@MockBean(name = "name", classes = { ExampleService.class, @MockBean(name = "name", classes = { ExampleService.class,
ExampleServiceCaller.class }) ExampleServiceCaller.class })
static class MultipleClassesWithName { static class MockBeanMultipleClassesWithName {
} }
static class InferClassToMock { static class MockBeanInferClassToMock {
@MockBean @MockBean
private ExampleService exampleService; private ExampleService exampleService;
...@@ -177,7 +238,54 @@ public class MockDefinitionsParserTests { ...@@ -177,7 +238,54 @@ public class MockDefinitionsParserTests {
} }
@MockBean @MockBean
static class MissingClassToMock { 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 {
} }
......
...@@ -36,7 +36,7 @@ import static org.mockito.BDDMockito.given; ...@@ -36,7 +36,7 @@ import static org.mockito.BDDMockito.given;
* @author Phillip Webb * @author Phillip Webb
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
public class OnConfigurationClassForExistingBeanIntegrationTests { public class MockBeanOnConfigurationClassForExistingBeanIntegrationTests {
@Autowired @Autowired
private ExampleServiceCaller caller; private ExampleServiceCaller caller;
......
...@@ -36,7 +36,7 @@ import static org.mockito.BDDMockito.given; ...@@ -36,7 +36,7 @@ import static org.mockito.BDDMockito.given;
* @author Phillip Webb * @author Phillip Webb
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
public class OnConfigurationClassForNewBeanIntegrationTests { public class MockBeanOnConfigurationClassForNewBeanIntegrationTests {
@Autowired @Autowired
private ExampleServiceCaller caller; private ExampleServiceCaller caller;
......
...@@ -37,7 +37,7 @@ import static org.mockito.BDDMockito.given; ...@@ -37,7 +37,7 @@ import static org.mockito.BDDMockito.given;
* @author Phillip Webb * @author Phillip Webb
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
public class OnConfigurationFieldForExistingBeanIntegrationTests { public class MockBeanOnConfigurationFieldForExistingBeanIntegrationTests {
@Autowired @Autowired
private Config config; private Config config;
......
...@@ -36,7 +36,7 @@ import static org.mockito.BDDMockito.given; ...@@ -36,7 +36,7 @@ import static org.mockito.BDDMockito.given;
* @author Phillip Webb * @author Phillip Webb
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
public class OnConfigurationFieldForNewBeanIntegrationTests { public class MockBeanOnConfigurationFieldForNewBeanIntegrationTests {
@Autowired @Autowired
private Config config; private Config config;
......
...@@ -21,8 +21,8 @@ import org.junit.runner.RunWith; ...@@ -21,8 +21,8 @@ import org.junit.runner.RunWith;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.OnContextHierarchyIntegrationTests.ChildConfig; import org.springframework.boot.test.mock.mockito.MockBeanOnContextHierarchyIntegrationTests.ChildConfig;
import org.springframework.boot.test.mock.mockito.OnContextHierarchyIntegrationTests.ParentConfig; 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.ExampleService;
import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller; import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
...@@ -42,7 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat; ...@@ -42,7 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@ContextHierarchy({ @ContextConfiguration(classes = ParentConfig.class), @ContextHierarchy({ @ContextConfiguration(classes = ParentConfig.class),
@ContextConfiguration(classes = ChildConfig.class) }) @ContextConfiguration(classes = ChildConfig.class) })
public class OnContextHierarchyIntegrationTests { public class MockBeanOnContextHierarchyIntegrationTests {
@Autowired @Autowired
private ChildConfig childConfig; private ChildConfig childConfig;
......
...@@ -37,7 +37,7 @@ import static org.mockito.BDDMockito.given; ...@@ -37,7 +37,7 @@ import static org.mockito.BDDMockito.given;
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@MockBean(ExampleService.class) @MockBean(ExampleService.class)
public class OnTestClassForExistingBeanIntegrationTests { public class MockBeanOnTestClassForExistingBeanIntegrationTests {
@Autowired @Autowired
private ExampleServiceCaller caller; private ExampleServiceCaller caller;
......
...@@ -36,7 +36,7 @@ import static org.mockito.BDDMockito.given; ...@@ -36,7 +36,7 @@ import static org.mockito.BDDMockito.given;
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@MockBean(ExampleService.class) @MockBean(ExampleService.class)
public class OnTestClassForNewBeanIntegrationTests { public class MockBeanOnTestClassForNewBeanIntegrationTests {
@Autowired @Autowired
private ExampleServiceCaller caller; private ExampleServiceCaller caller;
......
...@@ -31,15 +31,15 @@ import static org.mockito.BDDMockito.given; ...@@ -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 * 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 * the context is cached. This test is identical to
* {@link OnTestFieldForExistingBeanCacheIntegrationTests} so one of them should trigger * {@link MockBeanOnTestFieldForExistingBeanIntegrationTests} so one of them should
* application context caching. * trigger application context caching.
* *
* @author Phillip Webb * @author Phillip Webb
* @see OnTestFieldForExistingBeanIntegrationTests * @see MockBeanOnTestFieldForExistingBeanIntegrationTests
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@ContextConfiguration(classes = OnTestFieldForExistingBeanConfig.class) @ContextConfiguration(classes = MockBeanOnTestFieldForExistingBeanConfig.class)
public class OnTestFieldForExistingBeanCacheIntegrationTests { public class MockBeanOnTestFieldForExistingBeanCacheIntegrationTests {
@MockBean @MockBean
private ExampleService exampleService; private ExampleService exampleService;
......
...@@ -21,14 +21,14 @@ import org.springframework.context.annotation.Configuration; ...@@ -21,14 +21,14 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
/** /**
* Config for {@link OnTestFieldForExistingBeanIntegrationTests} and * Config for {@link MockBeanOnTestFieldForExistingBeanIntegrationTests} and
* {@link OnTestFieldForExistingBeanCacheIntegrationTests}. Extracted to a shared config * {@link MockBeanOnTestFieldForExistingBeanCacheIntegrationTests}. Extracted to a shared config
* to trigger caching. * to trigger caching.
* *
* @author Phillip Webb * @author Phillip Webb
*/ */
@Configuration @Configuration
@Import(ExampleServiceCaller.class) @Import(ExampleServiceCaller.class)
public class OnTestFieldForExistingBeanConfig { public class MockBeanOnTestFieldForExistingBeanConfig {
} }
...@@ -32,11 +32,11 @@ import static org.mockito.BDDMockito.given; ...@@ -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. * Test {@link MockBean} on a test class field can be used to replace existing beans.
* *
* @author Phillip Webb * @author Phillip Webb
* @see OnTestFieldForExistingBeanCacheIntegrationTests * @see MockBeanOnTestFieldForExistingBeanCacheIntegrationTests
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@ContextConfiguration(classes = OnTestFieldForExistingBeanConfig.class) @ContextConfiguration(classes = MockBeanOnTestFieldForExistingBeanConfig.class)
public class OnTestFieldForExistingBeanIntegrationTests { public class MockBeanOnTestFieldForExistingBeanIntegrationTests {
@MockBean @MockBean
private ExampleService exampleService; private ExampleService exampleService;
......
...@@ -36,7 +36,7 @@ import static org.mockito.BDDMockito.given; ...@@ -36,7 +36,7 @@ import static org.mockito.BDDMockito.given;
* @author Phillip Webb * @author Phillip Webb
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
public class OnTestFieldForNewBeanIntegrationTests { public class MockBeanOnTestFieldForNewBeanIntegrationTests {
@MockBean @MockBean
private ExampleService exampleService; private ExampleService exampleService;
......
...@@ -37,13 +37,13 @@ import static org.mockito.Mockito.mock; ...@@ -37,13 +37,13 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
/** /**
* Tests for {@link MockitoInitializeTestExecutionListener}. * Tests for {@link MockitoTestExecutionListener}.
* *
* @author Phillip Webb * @author Phillip Webb
*/ */
public class MockitoInitializeTestExecutionListenerTests { public class MockitoInitializeTestExecutionListenerTests {
private MockitoInitializeTestExecutionListener listener = new MockitoInitializeTestExecutionListener(); private MockitoTestExecutionListener listener = new MockitoTestExecutionListener();
@Mock @Mock
private ApplicationContext applicationContext; private ApplicationContext applicationContext;
......
...@@ -27,8 +27,7 @@ import org.springframework.context.annotation.Bean; ...@@ -27,8 +27,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
/** /**
* Test for {@link MockitoPostProcessor}. See also the integration tests in the * Test for {@link MockitoPostProcessor}. See also the integration tests.
* {@code runner} package.
* *
* @author Phillip Webb * @author Phillip Webb
*/ */
......
/*
* 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 {
}
}
/*
* 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 {
}
}
/*
* 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;
}
}
/*
* 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;
}
}
/*
* 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;
}
}
}
/*
* 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 {
}
}
/*
* 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 {
}
}
/*
* 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();
}
}
/*
* 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 {
}
/*
* 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();
}
}
/*
* 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 {
}
}
/*
* 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);
}
}
/*
* 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;
}
}
/*
* 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");
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment