Add @MockBean support

Add a `@MockBean` annotation which can be used to setup and inject mocks
into an application context. The annotation can be used on test classes,
test fields, configuration classes or configuration fields. When used on
a field the annotation also acts as an injection point.

Fixes gh-5042
This commit is contained in:
Phillip Webb 2016-02-22 12:04:58 -08:00
parent 0829a1bde8
commit 45c4f5f3f1
37 changed files with 2818 additions and 1 deletions

View File

@ -55,6 +55,11 @@
<artifactId>hamcrest-library</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>

View File

@ -0,0 +1,135 @@
/*
* 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.mockito.Answers;
import org.mockito.MockSettings;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AliasFor;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Annotation that can be used to add mocks 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>
* Mocks can be registered by type or by {@link #name() bean name}. Any existing single
* bean of the same type defined in the context will be replaced by the mock, if no
* existing bean is defined a new one will be added.
* <p>
* When {@code @MockBean} is used on a field, as well as being registered in the
* application context, the mock will also be injected into the field. Typical usage might
* be: <pre class="code">
* &#064;RunWith(SpringRunner.class)
* public class ExampleTests {
*
* &#064;MockBean
* private ExampleService service;
*
* &#064;Autowired
* private UserOfService userOfService;
*
* &#064;Test
* public void testUserOfService() {
* given(this.service.greet()).willReturn("Hello");
* String actual = this.userOfService.makeUse();
* assertEquals("Was: Hello", actual);
* }
*
* &#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 MockBeans @MockBeans} annotation.
*
* @author Phillip Webb
* @since 1.4.0
* @see MockitoPostProcessor
*/
@Target({ ElementType.TYPE, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(MockBeans.class)
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.
*/
String name() default "";
/**
* The classes to mock. This is an alias of {@link #classes()} which can be used for
* brevity if no other attributes are defined. See {@link #classes()} for details.
*/
@AliasFor("classes")
Class<?>[] value() default {};
/**
* The classes to mock. Each class specified here will result in a mock being created
* and registered with the application context. 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} alais
* instead.
*/
@AliasFor("value")
Class<?>[] classes() default {};
/**
* Any extra interfaces that should also be declared on the mock. See
* {@link MockSettings#extraInterfaces(Class...)} for details.
*/
Class<?>[] extraInterfaces() default {};
/**
* The {@link Answers} type to use on the mock.
*/
Answers answer() default Answers.RETURNS_DEFAULTS;
/**
* If the generated mock is serializable. See {@link MockSettings#serializable()} for
* details.
*/
boolean serializable() default false;
/**
* The reset mode to apply to the mock bean. The default is {@link MockReset#AFTER}
* meaning that mocks are automatically reset after each test method is invoked.
*/
MockReset reset() default MockReset.AFTER;
}

View File

@ -0,0 +1,46 @@
/*
* 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 MockBean} annotations.
* <p>
* Can be used natively, declaring several nested {@link MockBean} annotations. Can also
* be used in conjunction with Java 8's support for <em>repeatable annotations</em>, where
* {@link MockBean} 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 MockBeans {
/**
* Return the contained {@link MockBean} annotations.
*/
MockBean[] value();
}

View File

@ -0,0 +1,185 @@
/*
* 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.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.mockito.Answers;
import org.mockito.MockSettings;
import org.mockito.Mockito;
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 mock.
*
* @author Phillip Webb
*/
class MockDefinition {
private static final int MULTIPLIER = 31;
private final String name;
private final Class<?> classToMock;
private final Set<Class<?>> extraInterfaces;
private final Answers answer;
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) {
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<Class<?>> asClassSet(Class<?>[] classes) {
Set<Class<?>> classSet = new LinkedHashSet<Class<?>>();
if (classes != null) {
classSet.addAll(Arrays.asList(classes));
}
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}
*/
public Class<?> getClassToMock() {
return this.classToMock;
}
/**
* Return the extra interfaces.
* @return the extra interfaces or an empty array
*/
public Set<Class<?>> getExtraInterfaces() {
return this.extraInterfaces;
}
/**
* Return the answers mode.
* @return the answer the answers mode; never {@code null}
*/
public Answers getAnswer() {
return this.answer;
}
/**
* Return if the mock is serializable.
* @return if the mock is serializable
*/
public boolean isSerializable() {
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);
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;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || obj.getClass() != getClass()) {
return false;
}
MockDefinition other = (MockDefinition) obj;
boolean result = true;
result &= ObjectUtils.nullSafeEquals(this.name, other.name);
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 ToStringBuilder(this).append("name", this.name)
.append("classToMock", this.classToMock)
.append("extraInterfaces", this.extraInterfaces)
.append("answer", this.answer).append("serializable", this.serializable)
.append("reset", this.reset).build();
}
public <T> T createMock() {
return createMock(this.name);
}
@SuppressWarnings("unchecked")
public <T> T createMock(String name) {
MockSettings settings = MockReset.withSettings(this.reset);
if (StringUtils.hasLength(name)) {
settings.name(name);
}
if (!this.extraInterfaces.isEmpty()) {
settings.extraInterfaces(this.extraInterfaces.toArray(new Class<?>[] {}));
}
settings.defaultAnswer(this.answer.get());
if (this.serializable) {
settings.serializable();
}
return (T) Mockito.mock(this.classToMock, settings);
}
}

View File

@ -0,0 +1,117 @@
/*
* 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.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
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.
*
* @author Phillip Webb
*/
class MockDefinitionsParser {
private final Set<MockDefinition> definitions;
private final Map<MockDefinition, Field> fields;
MockDefinitionsParser() {
this(Collections.<MockDefinition>emptySet());
}
MockDefinitionsParser(Collection<? extends MockDefinition> existing) {
this.definitions = new LinkedHashSet<MockDefinition>();
this.fields = new LinkedHashMap<MockDefinition, Field>();
if (existing != null) {
this.definitions.addAll(existing);
}
}
public void parse(Class<?> source) {
parseElement(source);
ReflectionUtils.doWithFields(source, new FieldCallback() {
@Override
public void doWith(Field field)
throws IllegalArgumentException, IllegalAccessException {
parseElement(field);
}
});
}
private void parseElement(AnnotatedElement element) {
for (MockBean annotation : AnnotationUtils.getRepeatableAnnotations(element,
MockBean.class, MockBeans.class)) {
parseAnnotation(annotation, element);
}
}
private void parseAnnotation(MockBean annotation, AnnotatedElement element) {
Set<Class<?>> classesToMock = getOrDeduceClassesToMock(annotation, element);
Assert.state(!classesToMock.isEmpty(),
"Unable to deduce class to mock from " + element);
if (StringUtils.hasLength(annotation.name())) {
Assert.state(classesToMock.size() == 1,
"The name attribute can only be used when mocking a single class");
}
for (Class<?> classToMock : classesToMock) {
MockDefinition definition = new MockDefinition(annotation.name(), classToMock,
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);
}
}
}
private Set<Class<?>> getOrDeduceClassesToMock(MockBean annotation,
AnnotatedElement element) {
Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
classes.addAll(Arrays.asList(annotation.value()));
if (classes.isEmpty() && element instanceof Field) {
classes.add(((Field) element).getType());
}
return classes;
}
public Set<MockDefinition> getDefinitions() {
return Collections.unmodifiableSet(this.definitions);
}
public Field getField(MockDefinition definition) {
return this.fields.get(definition);
}
}

View File

@ -0,0 +1,140 @@
/*
* 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.List;
import org.mockito.MockSettings;
import org.mockito.Mockito;
import org.mockito.internal.util.MockUtil;
import org.mockito.listeners.InvocationListener;
import org.mockito.listeners.MethodInvocationReport;
import org.mockito.mock.MockCreationSettings;
import org.springframework.util.Assert;
/**
* Reset strategy used on a mock bean. Usually applied to a mock via the
* {@link MockBean @MockBean} annotation but can also be directly applied to any mock in
* the {@code ApplicationContext} using the static methods.
*
* @author Phillip Webb
* @see ResetMocksTestExecutionListener
*/
public enum MockReset {
/**
* Reset the mock before the test method runs.
*/
BEFORE,
/**
* Reset the mock after the test method runs.
*/
AFTER,
/**
* Don't reset the mock.
*/
NONE;
private static final MockUtil util = new MockUtil();
/**
* Create {@link MockSettings settings} to be use used with mocks where reset should
* occur before each test method runs.
* @return mock settings
*/
public static MockSettings before() {
return withSettings(BEFORE);
}
/**
* Create {@link MockSettings settings} to be use used with mocks where reset should
* occur after each test method runs.
* @return mock settings
*/
public static MockSettings after() {
return withSettings(AFTER);
}
/**
* Create {@link MockSettings settings} to be use used with mocks where a specific
* reset should occur.
* @param reset the reset type
* @return mock settings
*/
public static MockSettings withSettings(MockReset reset) {
return apply(reset, Mockito.withSettings());
}
/**
* Apply {@link MockReset} to existing {@link MockSettings settings}.
* @param reset the reset type
* @param settings the settings
* @return the configured settings
*/
public static MockSettings apply(MockReset reset, MockSettings settings) {
Assert.notNull(settings, "Settings must not be null");
if (reset != null && reset != NONE) {
settings.invocationListeners(new ResetInvocationListener(reset));
}
return settings;
}
/**
* Get the {@link MockReset} associated with the given mock.
* @param mock the source mock
* @return the reset type (never {@code null})
*/
@SuppressWarnings("rawtypes")
static MockReset get(Object mock) {
MockReset reset = MockReset.NONE;
if (util.isMock(mock)) {
MockCreationSettings settings = util.getMockSettings(mock);
List listeners = settings.getInvocationListeners();
for (Object listener : listeners) {
if (listener instanceof ResetInvocationListener) {
reset = ((ResetInvocationListener) listener).getReset();
}
}
}
return reset;
}
/**
* Dummy {@link InvocationListener} used to hold the {@link MockReset} value.
*/
private static class ResetInvocationListener implements InvocationListener {
private final MockReset reset;
ResetInvocationListener(MockReset reset) {
this.reset = reset;
}
public MockReset getReset() {
return this.reset;
}
@Override
public void reportInvocation(MethodInvocationReport methodInvocationReport) {
}
}
}

View File

@ -0,0 +1,65 @@
/*
* 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.Set;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.MergedContextConfiguration;
/**
* A {@link ContextCustomizer} to add Mockito support.
*
* @author Phillip Webb
*/
class MockitoContextCustomizer implements ContextCustomizer {
private final Set<MockDefinition> definitions;
MockitoContextCustomizer(Set<MockDefinition> definitions) {
this.definitions = definitions;
}
@Override
public void customizeContext(ConfigurableApplicationContext context,
MergedContextConfiguration mergedContextConfiguration) {
if (context instanceof BeanDefinitionRegistry) {
MockitoPostProcessor.register((BeanDefinitionRegistry) context,
this.definitions);
}
}
@Override
public int hashCode() {
return this.definitions.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || obj.getClass() != getClass()) {
return false;
}
MockitoContextCustomizer other = (MockitoContextCustomizer) obj;
return this.definitions.equals(other.definitions);
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.List;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
/**
* A {@link ContextCustomizerFactory} to add Mockito support.
*
* @author Phillip Webb
*/
class MockitoContextCustomizerFactory implements ContextCustomizerFactory {
@Override
public ContextCustomizer createContextCustomizer(Class<?> testClass,
List<ContextConfigurationAttributes> 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();
parser.parse(testClass);
return new MockitoContextCustomizer(parser.getDefinitions());
}
}

View File

@ -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 java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.LinkedHashSet;
import java.util.Set;
import org.mockito.Captor;
import org.mockito.MockitoAnnotations;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.support.AbstractTestExecutionListener;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;
/**
* {@link TestExecutionListener} to trigger {@link MockitoAnnotations#initMocks(Object)}
* when {@link MockBean @MockBean} annotations are used. Primarily to allow {@link Captor}
* annotations.
*
* @author Phillip Webb
*/
class MockitoInitializeTestExecutionListener extends AbstractTestExecutionListener {
@Override
public void prepareTestInstance(TestContext testContext) throws Exception {
if (hasMockitoAnnotations(testContext)) {
MockitoAnnotations.initMocks(testContext.getTestInstance());
}
injectFields(testContext);
}
private boolean hasMockitoAnnotations(TestContext testContext) {
MockitoAnnotationCollection collector = new MockitoAnnotationCollection();
ReflectionUtils.doWithFields(testContext.getTestClass(), collector);
return collector.hasAnnotations();
}
private void injectFields(TestContext testContext) {
MockDefinitionsParser parser = new MockDefinitionsParser();
parser.parse(testContext.getTestClass());
if (!parser.getDefinitions().isEmpty()) {
injectFields(testContext, parser);
}
}
private void injectFields(TestContext testContext, MockDefinitionsParser parser) {
ApplicationContext applicationContext = testContext.getApplicationContext();
MockitoPostProcessor postProcessor = applicationContext
.getBean(MockitoPostProcessor.class);
for (MockDefinition definition : parser.getDefinitions()) {
Field field = parser.getField(definition);
if (field != null) {
postProcessor.inject(field, testContext.getTestInstance(), definition);
}
}
}
/**
* {@link FieldCallback} to collect mockito annotations.
*/
private static class MockitoAnnotationCollection implements FieldCallback {
private final Set<Annotation> annotations = new LinkedHashSet<Annotation>();
@Override
public void doWith(Field field)
throws IllegalArgumentException, IllegalAccessException {
for (Annotation annotation : field.getDeclaredAnnotations()) {
if (annotation.annotationType().getName().startsWith("org.mockito")) {
this.annotations.add(annotation);
}
}
}
public boolean hasAnnotations() {
return !this.annotations.isEmpty();
}
}
}

View File

@ -0,0 +1,309 @@
/*
* 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.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanCreationException;
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.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.DefaultBeanNameGenerator;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.core.Conventions;
import org.springframework.core.Ordered;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;
import org.springframework.util.StringUtils;
/**
* A {@link BeanFactoryPostProcessor} used to register and inject
* {@link MockBean @MockBeans} with the {@link ApplicationContext}. An initial set of
* definitions can be passed to the processor with additional definitions being
* automatically created from {@code @Configuration} classes that use
* {@link MockBean @MockBean}.
*
* @author Phillip Webb
* @since 1.4.0
*/
public class MockitoPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
implements BeanClassLoaderAware, BeanFactoryAware, BeanFactoryPostProcessor,
Ordered {
private static final String BEAN_NAME = MockitoPostProcessor.class.getName();
private static final String CONFIGURATION_CLASS_ATTRIBUTE = Conventions
.getQualifiedAttributeName(ConfigurationClassPostProcessor.class,
"configurationClass");
private final Set<MockDefinition> mockDefinitions;
private ClassLoader classLoader;
private BeanFactory beanFactory;
private final BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator();
private Map<MockDefinition, String> beanNameRegistry = new HashMap<MockDefinition, String>();
private Map<Field, String> fieldRegistry = new HashMap<Field, String>();
/**
* Create a new {@link MockitoPostProcessor} instance with the given initial
* definitions.
* @param mockDefinitions the initial definitions
*/
public MockitoPostProcessor(Set<MockDefinition> mockDefinitions) {
this.mockDefinitions = mockDefinitions;
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory,
"Mock beans can only be used with a ConfigurableListableBeanFactory");
this.beanFactory = beanFactory;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
Assert.isInstanceOf(BeanDefinitionRegistry.class, beanFactory,
"@RegisterMocks can only be used on bean factories that "
+ "implement BeanDefinitionRegistry");
postProcessBeanFactory(beanFactory, (BeanDefinitionRegistry) beanFactory);
}
private void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory,
BeanDefinitionRegistry registry) {
MockDefinitionsParser parser = new MockDefinitionsParser(this.mockDefinitions);
for (Class<?> configurationClass : getConfigurationClasses(beanFactory)) {
parser.parse(configurationClass);
}
Set<MockDefinition> definitions = parser.getDefinitions();
for (MockDefinition definition : definitions) {
Field field = parser.getField(definition);
registerMock(beanFactory, registry, definition, field);
}
}
private Set<Class<?>> getConfigurationClasses(
ConfigurableListableBeanFactory beanFactory) {
Set<Class<?>> configurationClasses = new LinkedHashSet<Class<?>>();
for (BeanDefinition beanDefinition : getConfigurationBeanDefinitions(beanFactory)
.values()) {
configurationClasses.add(ClassUtils.resolveClassName(
beanDefinition.getBeanClassName(), this.classLoader));
}
return configurationClasses;
}
private Map<String, BeanDefinition> getConfigurationBeanDefinitions(
ConfigurableListableBeanFactory beanFactory) {
Map<String, BeanDefinition> definitions = new LinkedHashMap<String, BeanDefinition>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
if (definition.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE) != null) {
definitions.put(beanName, definition);
}
}
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 registerMock(ConfigurableListableBeanFactory beanFactory,
BeanDefinitionRegistry registry, MockDefinition mockDefinition, Field field) {
RootBeanDefinition beanDefinition = createBeanDefinition(mockDefinition);
String name = getBeanName(beanFactory, registry, mockDefinition, beanDefinition);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(1, name);
registry.registerBeanDefinition(name, beanDefinition);
this.beanNameRegistry.put(mockDefinition, name);
if (field != null) {
this.fieldRegistry.put(field, name);
}
}
private RootBeanDefinition createBeanDefinition(MockDefinition mockDefinition) {
RootBeanDefinition definition = new RootBeanDefinition(
mockDefinition.getClassToMock());
definition.setTargetType(mockDefinition.getClassToMock());
definition.setFactoryBeanName(BEAN_NAME);
definition.setFactoryMethodName("createMock");
definition.getConstructorArgumentValues().addIndexedArgumentValue(0,
mockDefinition);
return definition;
}
/**
* Factory method used by defined beans to actually create the mock.
* @param definition 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");
}
private String getBeanName(ConfigurableListableBeanFactory beanFactory,
BeanDefinitionRegistry registry, MockDefinition mockDefinition,
RootBeanDefinition beanDefinition) {
if (StringUtils.hasLength(mockDefinition.getName())) {
return mockDefinition.getName();
}
String[] existingBeans = beanFactory
.getBeanNamesForType(mockDefinition.getClassToMock());
if (ObjectUtils.isEmpty(existingBeans)) {
return this.beanNameGenerator.generateBeanName(beanDefinition, registry);
}
if (existingBeans.length == 1) {
return existingBeans[0];
}
throw new IllegalStateException("Unable to register mock bean "
+ mockDefinition.getClassToMock().getName()
+ " expected a single existing bean to replace but found "
+ new TreeSet<String>(Arrays.asList(existingBeans)));
}
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs,
PropertyDescriptor[] pds, final Object bean, String beanName)
throws BeansException {
ReflectionUtils.doWithFields(bean.getClass(), new FieldCallback() {
@Override
public void doWith(Field field)
throws IllegalArgumentException, IllegalAccessException {
postProcessField(bean, field);
}
});
return pvs;
}
private void postProcessField(Object bean, Field field) {
String beanName = this.fieldRegistry.get(field);
if (StringUtils.hasLength(beanName)) {
injectMock(field, bean, beanName);
}
}
private void injectMock(Field field, Object target, String beanName) {
try {
field.setAccessible(true);
Object mockBean = this.beanFactory.getBean(beanName, field.getType());
ReflectionUtils.setField(field, target, mockBean);
}
catch (Throwable ex) {
throw new BeanCreationException("Could not inject mock field: " + field, ex);
}
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 10;
}
/**
* Register the processor with a {@link BeanDefinitionRegistry}. Not required when
* using the {@link SpringRunner} as registration is automatic.
* @param registry the bean definition registry
*/
public static void register(BeanDefinitionRegistry registry) {
register(registry, null);
}
/**
* 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
*/
public static void register(BeanDefinitionRegistry registry,
Set<MockDefinition> mockDefinitions) {
register(registry, MockitoPostProcessor.class, mockDefinitions);
}
/**
* 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 postProcessor the post processor class to register
* @param mockDefinitions the initial mock definitions
*/
@SuppressWarnings("unchecked")
public static void register(BeanDefinitionRegistry registry,
Class<? extends MockitoPostProcessor> postProcessor,
Set<MockDefinition> mockDefinitions) {
BeanDefinition definition = getOrAddBeanDefinition(registry, postProcessor);
ValueHolder constructorArg = definition.getConstructorArgumentValues()
.getIndexedArgumentValue(0, Set.class);
Set<MockDefinition> existing = (Set<MockDefinition>) constructorArg.getValue();
if (mockDefinitions != null) {
existing.addAll(mockDefinitions);
}
}
private static BeanDefinition getOrAddBeanDefinition(BeanDefinitionRegistry registry,
Class<? extends MockitoPostProcessor> postProcessor) {
if (!registry.containsBeanDefinition(BEAN_NAME)) {
RootBeanDefinition definition = new RootBeanDefinition(postProcessor);
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
ConstructorArgumentValues constructorArguments = definition
.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0,
new LinkedHashSet<MockDefinition>());
registry.registerBeanDefinition(BEAN_NAME, definition);
return definition;
}
return registry.getBeanDefinition(BEAN_NAME);
}
}

View File

@ -0,0 +1,74 @@
/*
* 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.Mockito;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.support.AbstractTestExecutionListener;
/**
* {@link TestExecutionListener} to reset any mock beans that have been marked with a
* {@link MockReset}.
*
* @author Phillip Webb
* @since 1.4.0
*/
public class ResetMocksTestExecutionListener extends AbstractTestExecutionListener {
@Override
public void beforeTestMethod(TestContext testContext) throws Exception {
resetMocks(testContext.getApplicationContext(), MockReset.BEFORE);
}
@Override
public void afterTestMethod(TestContext testContext) throws Exception {
resetMocks(testContext.getApplicationContext(), MockReset.AFTER);
}
private void resetMocks(ApplicationContext applicationContext, MockReset reset) {
if (applicationContext instanceof ConfigurableApplicationContext) {
resetMocks((ConfigurableApplicationContext) applicationContext, reset);
}
}
private void resetMocks(ConfigurableApplicationContext applicationContext,
MockReset reset) {
ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
String[] names = beanFactory.getBeanDefinitionNames();
for (String name : names) {
BeanDefinition definition = beanFactory.getBeanDefinition(name);
if (AbstractBeanDefinition.SCOPE_DEFAULT.equals(definition.getScope())) {
Object bean = beanFactory.getBean(name);
if (reset.equals(MockReset.get(bean))) {
Mockito.reset(bean);
}
}
}
if (applicationContext.getParent() != null) {
resetMocks(applicationContext.getParent(), reset);
}
}
}

View File

@ -0,0 +1,20 @@
/*
* 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.
*/
/**
* Mockito integration for Spring Boot tests.
*/
package org.springframework.boot.test.mock.mockito;

View File

@ -1,3 +1,9 @@
# Spring Test ContextCustomizerFactories
org.springframework.test.context.ContextCustomizerFactory=\
org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizerFactory
org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizerFactory,\
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.ResetMocksTestExecutionListener

View File

@ -0,0 +1,91 @@
/*
* 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.ExampleExtraInterface;
import org.springframework.boot.test.mock.mockito.example.ExampleService;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link MockDefinition}.
*
* @author Phillip Webb
*/
public class MockDefinitionTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void ClassToMockMustNotBeNull() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("ClassToMock must not be null");
new MockDefinition(null, null, null, null, false, null);
}
@Test
public void createWithDefaults() throws Exception {
MockDefinition definition = new MockDefinition(null, ExampleService.class, null,
null, false, null);
assertThat(definition.getName()).isNull();
assertThat(definition.getClassToMock()).isEqualTo(ExampleService.class);
assertThat(definition.getExtraInterfaces()).isEmpty();
assertThat(definition.getAnswer()).isEqualTo(Answers.RETURNS_DEFAULTS);
assertThat(definition.isSerializable()).isFalse();
assertThat(definition.getReset()).isEqualTo(MockReset.AFTER);
}
@Test
public void createExplicit() throws Exception {
MockDefinition definition = new MockDefinition("name", ExampleService.class,
new Class<?>[] { ExampleExtraInterface.class },
Answers.RETURNS_SMART_NULLS, true, MockReset.BEFORE);
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()).isTrue();
assertThat(definition.getReset()).isEqualTo(MockReset.BEFORE);
}
@Test
public void createMock() throws Exception {
MockDefinition definition = new MockDefinition("name", ExampleService.class,
new Class<?>[] { ExampleExtraInterface.class },
Answers.RETURNS_SMART_NULLS, true, MockReset.BEFORE);
ExampleService mock = definition.createMock();
MockCreationSettings<?> settings = new MockUtil().getMockSettings(mock);
assertThat(mock).isInstanceOf(ExampleService.class);
assertThat(mock).isInstanceOf(ExampleExtraInterface.class);
assertThat(settings.getMockName().toString()).isEqualTo("name");
assertThat(settings.getDefaultAnswer())
.isEqualTo(Answers.RETURNS_SMART_NULLS.get());
assertThat(settings.isSerializable()).isTrue();
assertThat(MockReset.get(mock)).isEqualTo(MockReset.BEFORE);
}
}

View File

@ -0,0 +1,184 @@
/*
* 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<MockDefinition> getDefinitions() {
return new ArrayList<MockDefinition>(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 {
}
}

View File

@ -0,0 +1,73 @@
/*
* 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.springframework.boot.test.mock.mockito.example.ExampleService;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.withSettings;
/**
* Tests for {@link MockReset}.
*
* @author Phillip Webb
*/
public class MockResetTests {
@Test
public void noneAttachesReset() {
ExampleService mock = mock(ExampleService.class);
assertThat(MockReset.get(mock)).isEqualTo(MockReset.NONE);
}
@Test
public void withSettingsOfNoneAttachesReset() {
ExampleService mock = mock(ExampleService.class,
MockReset.withSettings(MockReset.NONE));
assertThat(MockReset.get(mock)).isEqualTo(MockReset.NONE);
}
@Test
public void beforeAttachesReset() {
ExampleService mock = mock(ExampleService.class, MockReset.before());
assertThat(MockReset.get(mock)).isEqualTo(MockReset.BEFORE);
}
@Test
public void afterAttachesReset() {
ExampleService mock = mock(ExampleService.class, MockReset.after());
assertThat(MockReset.get(mock)).isEqualTo(MockReset.AFTER);
}
@Test
public void withSettingsAttachesReset() {
ExampleService mock = mock(ExampleService.class,
MockReset.withSettings(MockReset.BEFORE));
assertThat(MockReset.get(mock)).isEqualTo(MockReset.BEFORE);
}
@Test
public void apply() throws Exception {
ExampleService mock = mock(ExampleService.class,
MockReset.apply(MockReset.AFTER, withSettings()));
assertThat(MockReset.get(mock)).isEqualTo(MockReset.AFTER);
}
}

View File

@ -0,0 +1,97 @@
/*
* 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.mockito.runners.MockitoJUnitRunner;
import org.springframework.test.context.ContextCustomizer;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link MockitoContextCustomizerFactory}.
*
* @author Phillip Webb
*/
@RunWith(MockitoJUnitRunner.class)
public class MockitoContextCustomizerFactoryTests {
private final MockitoContextCustomizerFactory factory = new MockitoContextCustomizerFactory();
@Test
public void getContextCustomizerWithoutAnnotationReturnsCustomizer()
throws Exception {
ContextCustomizer customizer = this.factory
.createContextCustomizer(NoRegisterMocksAnnotation.class, null);
assertThat(customizer).isNotNull();
}
@Test
public void getContextCustomizerWithAnnotationReturnsCustomizer() throws Exception {
ContextCustomizer customizer = this.factory
.createContextCustomizer(WithRegisterMocksAnnotation.class, null);
assertThat(customizer).isNotNull();
}
@Test
public void getContextCustomizerUsesMocksAsCacheKey() throws Exception {
ContextCustomizer customizer = this.factory
.createContextCustomizer(WithRegisterMocksAnnotation.class, null);
assertThat(customizer).isNotNull();
ContextCustomizer same = this.factory
.createContextCustomizer(WithSameRegisterMocksAnnotation.class, null);
assertThat(customizer).isNotNull();
ContextCustomizer different = this.factory.createContextCustomizer(
WithDifferentRegisterMocksAnnotation.class, null);
assertThat(customizer).isNotNull();
assertThat(customizer.hashCode()).isEqualTo(same.hashCode());
assertThat(customizer.hashCode()).isNotEqualTo(different.hashCode());
assertThat(customizer).isEqualTo(customizer);
assertThat(customizer).isEqualTo(same);
assertThat(customizer).isNotEqualTo(different);
}
static class NoRegisterMocksAnnotation {
}
@MockBean({ Service1.class, Service2.class })
static class WithRegisterMocksAnnotation {
}
@MockBean({ Service2.class, Service1.class })
static class WithSameRegisterMocksAnnotation {
}
@MockBean({ Service1.class })
static class WithDifferentRegisterMocksAnnotation {
}
interface Service1 {
}
interface Service2 {
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.junit.Test;
import org.springframework.boot.test.mock.mockito.example.ExampleService;
import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link MockitoContextCustomizer}.
*
* @author Phillip Webb
*/
public class MockitoContextCustomizerTests {
private static final Set<MockDefinition> NO_DEFINITIONS = Collections.emptySet();
@Test
public void hashCodeAndEquals() {
MockDefinition d1 = new MockDefinition(ExampleService.class);
MockDefinition d2 = new MockDefinition(ExampleServiceCaller.class);
MockitoContextCustomizer c1 = new MockitoContextCustomizer(NO_DEFINITIONS);
MockitoContextCustomizer c2 = new MockitoContextCustomizer(
new LinkedHashSet<MockDefinition>(Arrays.asList(d1, d2)));
MockitoContextCustomizer c3 = new MockitoContextCustomizer(
new LinkedHashSet<MockDefinition>(Arrays.asList(d2, d1)));
assertThat(c2.hashCode()).isEqualTo(c3.hashCode());
assertThat(c1).isEqualTo(c1).isNotEqualTo(c2);
assertThat(c2).isEqualTo(c2).isEqualTo(c3).isNotEqualTo(c1);
}
}

View File

@ -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 java.io.InputStream;
import java.lang.reflect.Field;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.TestContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link MockitoInitializeTestExecutionListener}.
*
* @author Phillip Webb
*/
public class MockitoInitializeTestExecutionListenerTests {
private MockitoInitializeTestExecutionListener listener = new MockitoInitializeTestExecutionListener();
@Mock
private ApplicationContext applicationContext;
@Mock
private MockitoPostProcessor postProcessor;
@Captor
private ArgumentCaptor<Field> fieldCaptor;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
given(this.applicationContext.getBean(MockitoPostProcessor.class))
.willReturn(this.postProcessor);
}
@Test
public void prepareTestInstanceShouldInitMockitoAnnotation() throws Exception {
WithMockitoAnnotation instance = new WithMockitoAnnotation();
this.listener.prepareTestInstance(mockTestContext(instance));
assertThat(instance.mock).isNotNull();
assertThat(instance.captor).isNotNull();
}
@Test
public void prepareTestInstanceShouldInjectMockBeans() throws Exception {
WithMockBeans instance = new WithMockBeans();
this.listener.prepareTestInstance(mockTestContext(instance));
verify(this.postProcessor).inject(this.fieldCaptor.capture(), eq(instance),
(MockDefinition) any());
assertThat(this.fieldCaptor.getValue().getName()).isEqualTo("mockBean");
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private TestContext mockTestContext(Object instance) {
TestContext testContext = mock(TestContext.class);
given(testContext.getTestInstance()).willReturn(instance);
given(testContext.getTestClass()).willReturn((Class) instance.getClass());
given(testContext.getApplicationContext()).willReturn(this.applicationContext);
return testContext;
}
static class WithMockitoAnnotation {
@Mock
InputStream mock;
@Captor
ArgumentCaptor<InputStream> captor;
}
static class WithMockBeans {
@MockBean
InputStream mockBean;
}
}

View File

@ -0,0 +1,69 @@
/*
* 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.springframework.boot.test.mock.mockito.example.ExampleService;
import org.springframework.boot.test.mock.mockito.example.FailingExampleService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
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.
*
* @author Phillip Webb
*/
public class MockitoPostProcessorTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void cannotMockMulipleBeans() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
MockitoPostProcessor.register(context);
context.register(MultipleBeans.class);
this.thrown.expect(IllegalStateException.class);
this.thrown.expectMessage(
"Unable to register mock bean " + ExampleService.class.getName()
+ " expected a single existing bean to replace "
+ "but found [example1, example2]");
context.refresh();
}
@Configuration
@MockBean(ExampleService.class)
static class MultipleBeans {
@Bean
public ExampleService example1() {
return new FailingExampleService();
}
@Bean
public ExampleService example2() {
return new FailingExampleService();
}
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.FailingExampleService;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
/**
* Test {@link MockBean} on a configuration class can be used to replace existing beans.
*
* @author Phillip Webb
*/
@RunWith(SpringRunner.class)
public class OnConfigurationClassForExistingBeanIntegrationTests {
@Autowired
private ExampleServiceCaller caller;
@Test
public void testMocking() throws Exception {
given(this.caller.getService().greeting()).willReturn("Boot");
assertThat(this.caller.sayGreeting()).isEqualTo("I say Boot");
}
@Configuration
@MockBean(ExampleService.class)
@Import({ ExampleServiceCaller.class, FailingExampleService.class })
static class Config {
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
/**
* Test {@link MockBean} on a configuration class can be used to inject new mock
* instances.
*
* @author Phillip Webb
*/
@RunWith(SpringRunner.class)
public class OnConfigurationClassForNewBeanIntegrationTests {
@Autowired
private ExampleServiceCaller caller;
@Test
public void testMocking() throws Exception {
given(this.caller.getService().greeting()).willReturn("Boot");
assertThat(this.caller.sayGreeting()).isEqualTo("I say Boot");
}
@Configuration
@MockBean(ExampleService.class)
@Import(ExampleServiceCaller.class)
static class Config {
}
}

View File

@ -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.FailingExampleService;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
/**
* Test {@link MockBean} on a field on a {@code @Configuration} class can be used to
* replace existing beans.
*
* @author Phillip Webb
*/
@RunWith(SpringRunner.class)
public class OnConfigurationFieldForExistingBeanIntegrationTests {
@Autowired
private Config config;
@Autowired
private ExampleServiceCaller caller;
@Test
public void testMocking() throws Exception {
given(this.config.exampleService.greeting()).willReturn("Boot");
assertThat(this.caller.sayGreeting()).isEqualTo("I say Boot");
}
@Configuration
@Import({ ExampleServiceCaller.class, FailingExampleService.class })
static class Config {
@MockBean
private ExampleService exampleService;
}
}

View File

@ -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.ExampleService;
import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
/**
* Test {@link MockBean} on a field on a {@code @Configuration} class can be used to
* inject new mock instances.
*
* @author Phillip Webb
*/
@RunWith(SpringRunner.class)
public class OnConfigurationFieldForNewBeanIntegrationTests {
@Autowired
private Config config;
@Autowired
private ExampleServiceCaller caller;
@Test
public void testMocking() throws Exception {
given(this.config.exampleService.greeting()).willReturn("Boot");
assertThat(this.caller.sayGreeting()).isEqualTo("I say Boot");
}
@Configuration
@Import(ExampleServiceCaller.class)
static class Config {
@MockBean
private ExampleService exampleService;
}
}

View File

@ -0,0 +1,87 @@
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test.mock.mockito;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.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.example.ExampleService;
import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller;
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 MockBean} can be used with a {@link ContextHierarchy}.
*
* @author Phillip Webb
*/
@RunWith(SpringRunner.class)
@ContextHierarchy({ @ContextConfiguration(classes = ParentConfig.class),
@ContextConfiguration(classes = ChildConfig.class) })
public class OnContextHierarchyIntegrationTests {
@Autowired
private ChildConfig childConfig;
@Test
public void testMocking() 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
@MockBean(ExampleService.class)
static class ParentConfig {
}
@Configuration
@MockBean(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;
}
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.FailingExampleService;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
/**
* Test {@link MockBean} on a test class can be used to replace existing beans.
*
* @author Phillip Webb
*/
@RunWith(SpringRunner.class)
@MockBean(ExampleService.class)
public class OnTestClassForExistingBeanIntegrationTests {
@Autowired
private ExampleServiceCaller caller;
@Test
public void testMocking() throws Exception {
given(this.caller.getService().greeting()).willReturn("Boot");
assertThat(this.caller.sayGreeting()).isEqualTo("I say Boot");
}
@Configuration
@Import({ ExampleServiceCaller.class, FailingExampleService.class })
static class Config {
}
}

View File

@ -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.ExampleService;
import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
/**
* Test {@link MockBean} on a test class can be used to inject new mock instances.
*
* @author Phillip Webb
*/
@RunWith(SpringRunner.class)
@MockBean(ExampleService.class)
public class OnTestClassForNewBeanIntegrationTests {
@Autowired
private ExampleServiceCaller caller;
@Test
public void testMocking() throws Exception {
given(this.caller.getService().greeting()).willReturn("Boot");
assertThat(this.caller.sayGreeting()).isEqualTo("I say Boot");
}
@Configuration
@Import(ExampleServiceCaller.class)
static class Config {
}
}

View File

@ -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.ExampleService;
import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller;
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.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.
*
* @author Phillip Webb
* @see OnTestFieldForExistingBeanIntegrationTests
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = OnTestFieldForExistingBeanConfig.class)
public class OnTestFieldForExistingBeanCacheIntegrationTests {
@MockBean
private ExampleService exampleService;
@Autowired
private ExampleServiceCaller caller;
@Test
public void testMocking() throws Exception {
given(this.exampleService.greeting()).willReturn("Boot");
assertThat(this.caller.sayGreeting()).isEqualTo("I say Boot");
}
}

View File

@ -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 OnTestFieldForExistingBeanIntegrationTests} and
* {@link OnTestFieldForExistingBeanCacheIntegrationTests}. Extracted to a shared config
* to trigger caching.
*
* @author Phillip Webb
*/
@Configuration
@Import(ExampleServiceCaller.class)
public class OnTestFieldForExistingBeanConfig {
}

View File

@ -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.ExampleService;
import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller;
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.BDDMockito.given;
/**
* Test {@link MockBean} on a test class field can be used to replace existing beans.
*
* @author Phillip Webb
* @see OnTestFieldForExistingBeanCacheIntegrationTests
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = OnTestFieldForExistingBeanConfig.class)
public class OnTestFieldForExistingBeanIntegrationTests {
@MockBean
private ExampleService exampleService;
@Autowired
private ExampleServiceCaller caller;
@Test
public void testMocking() throws Exception {
given(this.exampleService.greeting()).willReturn("Boot");
assertThat(this.caller.sayGreeting()).isEqualTo("I say Boot");
}
}

View File

@ -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.FailingExampleService;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
/**
* Test {@link MockBean} on a test class field can be used to inject new mock instances.
*
* @author Phillip Webb
*/
@RunWith(SpringRunner.class)
public class OnTestFieldForNewBeanIntegrationTests {
@MockBean
private ExampleService exampleService;
@Autowired
private ExampleServiceCaller caller;
@Test
public void testMocking() throws Exception {
given(this.exampleService.greeting()).willReturn("Boot");
assertThat(this.caller.sayGreeting()).isEqualTo("I say Boot");
}
@Configuration
@Import({ ExampleServiceCaller.class, FailingExampleService.class })
static class Config {
}
}

View File

@ -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.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.example.ExampleService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link ResetMocksTestExecutionListener}.
*
* @author Phillip Webb
*/
@RunWith(SpringRunner.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ResetMocksTestExecutionListenerTests {
static boolean test001executed;
@Autowired
private ApplicationContext context;
@Test
public void test001() {
given(getMock("none").greeting()).willReturn("none");
given(getMock("before").greeting()).willReturn("before");
given(getMock("after").greeting()).willReturn("after");
test001executed = true;
}
@Test
public void test002() {
assertThat(getMock("none").greeting()).isEqualTo("none");
assertThat(getMock("before").greeting()).isNull();
assertThat(getMock("after").greeting()).isNull();
}
public ExampleService getMock(String name) {
return this.context.getBean(name, ExampleService.class);
}
@Configuration
static class Config {
@Bean
public ExampleService before() {
return mock(ExampleService.class, MockReset.before());
}
@Bean
public ExampleService after() {
return mock(ExampleService.class, MockReset.before());
}
@Bean
public ExampleService none() {
return mock(ExampleService.class);
}
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test.mock.mockito.example;
/**
* Example extra interface for mocking tests.
*
* @author Phillip Webb
*/
public interface ExampleExtraInterface {
String doExtra();
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test.mock.mockito.example;
/**
* Example service interface for mocking tests.
*
* @author Phillip Webb
*/
public interface ExampleService {
String greeting();
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test.mock.mockito.example;
import java.security.Provider.Service;
/**
* Example bean for mocking tests that calls {@link Service}.
*
* @author Phillip Webb
*/
public class ExampleServiceCaller {
private final ExampleService service;
public ExampleServiceCaller(ExampleService service) {
this.service = service;
}
public ExampleService getService() {
return this.service;
}
public String sayGreeting() {
return "I say " + this.service.greeting();
}
}

View File

@ -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.example;
import org.springframework.stereotype.Service;
/**
* An {@link ExampleService} that always throws an exception.
*
* @author Phillip Webb
*/
@Service
public class FailingExampleService implements ExampleService {
@Override
public String greeting() {
throw new IllegalStateException("Failed");
}
}

View File

@ -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;
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;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.core.annotation.AliasFor;
@Target({ ElementType.TYPE, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@MockBean
public @interface MyMockBean {
@AliasFor(annotation = MockBean.class, attribute = "value")
Class<?> value();
}