org.springframework
spring-test
diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockBean.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockBean.java
new file mode 100644
index 00000000000..f6d3640375b
--- /dev/null
+++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockBean.java
@@ -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}.
+ *
+ * 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.
+ *
+ * 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:
+ * @RunWith(SpringRunner.class)
+ * public class ExampleTests {
+ *
+ * @MockBean
+ * private ExampleService service;
+ *
+ * @Autowired
+ * private UserOfService userOfService;
+ *
+ * @Test
+ * public void testUserOfService() {
+ * given(this.service.greet()).willReturn("Hello");
+ * String actual = this.userOfService.makeUse();
+ * assertEquals("Was: Hello", actual);
+ * }
+ *
+ * @Configuration
+ * @Import(UserOfService.class) // A @Component injected with ExampleService
+ * static class Config {
+ * }
+ *
+ *
+ * }
+ *
+ *
+ * This annotation is {@code @Repeatable} and may be specified multiple times when working
+ * with Java 8 or contained within an {@link 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.
+ *
+ * When {@code @MockBean} also defines a {@code name} this attribute can only contain
+ * a single value.
+ *
+ * If this is the only attribute specified consider using the {@code value} 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;
+
+}
diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockBeans.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockBeans.java
new file mode 100644
index 00000000000..a24cd2779bb
--- /dev/null
+++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockBeans.java
@@ -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.
+ *
+ * Can be used natively, declaring several nested {@link MockBean} annotations. Can also
+ * be used in conjunction with Java 8's support for repeatable annotations, 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();
+
+}
diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockDefinition.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockDefinition.java
new file mode 100644
index 00000000000..cc0e223137b
--- /dev/null
+++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockDefinition.java
@@ -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> 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> asClassSet(Class>[] classes) {
+ Set> classSet = new LinkedHashSet>();
+ 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> 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 createMock() {
+ return createMock(this.name);
+ }
+
+ @SuppressWarnings("unchecked")
+ public 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);
+ }
+
+}
diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockDefinitionsParser.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockDefinitionsParser.java
new file mode 100644
index 00000000000..06fac9c8a47
--- /dev/null
+++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockDefinitionsParser.java
@@ -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 definitions;
+
+ private final Map fields;
+
+ MockDefinitionsParser() {
+ this(Collections.emptySet());
+ }
+
+ MockDefinitionsParser(Collection extends MockDefinition> existing) {
+ this.definitions = new LinkedHashSet();
+ this.fields = new LinkedHashMap();
+ 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> 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> getOrDeduceClassesToMock(MockBean annotation,
+ AnnotatedElement element) {
+ Set> classes = new LinkedHashSet>();
+ classes.addAll(Arrays.asList(annotation.value()));
+ if (classes.isEmpty() && element instanceof Field) {
+ classes.add(((Field) element).getType());
+ }
+ return classes;
+ }
+
+ public Set getDefinitions() {
+ return Collections.unmodifiableSet(this.definitions);
+ }
+
+ public Field getField(MockDefinition definition) {
+ return this.fields.get(definition);
+ }
+
+}
diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockReset.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockReset.java
new file mode 100644
index 00000000000..f431c2a33c0
--- /dev/null
+++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockReset.java
@@ -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) {
+ }
+
+ }
+
+}
diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizer.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizer.java
new file mode 100644
index 00000000000..965ba888279
--- /dev/null
+++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizer.java
@@ -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 definitions;
+
+ MockitoContextCustomizer(Set 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);
+ }
+
+}
diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizerFactory.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizerFactory.java
new file mode 100644
index 00000000000..689302f796b
--- /dev/null
+++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizerFactory.java
@@ -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 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());
+ }
+
+}
diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoInitializeTestExecutionListener.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoInitializeTestExecutionListener.java
new file mode 100644
index 00000000000..ff200e63a4e
--- /dev/null
+++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoInitializeTestExecutionListener.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.test.mock.mockito;
+
+import 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 annotations = new LinkedHashSet();
+
+ @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();
+ }
+
+ }
+
+}
diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessor.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessor.java
new file mode 100644
index 00000000000..61abdefeb81
--- /dev/null
+++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessor.java
@@ -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 mockDefinitions;
+
+ private ClassLoader classLoader;
+
+ private BeanFactory beanFactory;
+
+ private final BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator();
+
+ private Map beanNameRegistry = new HashMap();
+
+ private Map fieldRegistry = new HashMap();
+
+ /**
+ * Create a new {@link MockitoPostProcessor} instance with the given initial
+ * definitions.
+ * @param mockDefinitions the initial definitions
+ */
+ public MockitoPostProcessor(Set 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 definitions = parser.getDefinitions();
+ for (MockDefinition definition : definitions) {
+ Field field = parser.getField(definition);
+ registerMock(beanFactory, registry, definition, field);
+ }
+ }
+
+ private Set> getConfigurationClasses(
+ ConfigurableListableBeanFactory beanFactory) {
+ Set> configurationClasses = new LinkedHashSet>();
+ for (BeanDefinition beanDefinition : getConfigurationBeanDefinitions(beanFactory)
+ .values()) {
+ configurationClasses.add(ClassUtils.resolveClassName(
+ beanDefinition.getBeanClassName(), this.classLoader));
+ }
+ return configurationClasses;
+ }
+
+ private Map getConfigurationBeanDefinitions(
+ ConfigurableListableBeanFactory beanFactory) {
+ Map definitions = new LinkedHashMap();
+ 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(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 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 mockDefinitions) {
+ BeanDefinition definition = getOrAddBeanDefinition(registry, postProcessor);
+ ValueHolder constructorArg = definition.getConstructorArgumentValues()
+ .getIndexedArgumentValue(0, Set.class);
+ Set existing = (Set) 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());
+ registry.registerBeanDefinition(BEAN_NAME, definition);
+ return definition;
+ }
+ return registry.getBeanDefinition(BEAN_NAME);
+ }
+
+}
diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/ResetMocksTestExecutionListener.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/ResetMocksTestExecutionListener.java
new file mode 100644
index 00000000000..81f8535d771
--- /dev/null
+++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/ResetMocksTestExecutionListener.java
@@ -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);
+ }
+ }
+
+}
diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/package-info.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/package-info.java
new file mode 100644
index 00000000000..15eb6ec7d8c
--- /dev/null
+++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/package-info.java
@@ -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;
diff --git a/spring-boot-test/src/main/resources/META-INF/spring.factories b/spring-boot-test/src/main/resources/META-INF/spring.factories
index 08e4fa5ad5e..90927f93b90 100644
--- a/spring-boot-test/src/main/resources/META-INF/spring.factories
+++ b/spring-boot-test/src/main/resources/META-INF/spring.factories
@@ -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
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockDefinitionTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockDefinitionTests.java
new file mode 100644
index 00000000000..3dbcd47bbbb
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockDefinitionTests.java
@@ -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);
+
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockDefinitionsParserTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockDefinitionsParserTests.java
new file mode 100644
index 00000000000..176026d0e64
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockDefinitionsParserTests.java
@@ -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 getDefinitions() {
+ return new ArrayList(this.parser.getDefinitions());
+ }
+
+ @MockBean(ExampleService.class)
+ static class SingleMockBean {
+
+ }
+
+ @MockBeans({ @MockBean(ExampleService.class), @MockBean(ExampleServiceCaller.class) })
+ static class RepeatMockBean {
+
+ }
+
+ @MyMockBean(ExampleService.class)
+ static class MetaMockBean {
+
+ }
+
+ @MockBean(name = "Name", classes = ExampleService.class, extraInterfaces = ExampleExtraInterface.class, answer = Answers.RETURNS_SMART_NULLS, serializable = true, reset = MockReset.NONE)
+ static class MockBeanAttributes {
+
+ }
+
+ @MockBean(ExampleService.class)
+ static class OnClassAndField {
+
+ @MockBean(ExampleServiceCaller.class)
+ private Object caller;
+
+ }
+
+ @MockBean({ ExampleService.class, ExampleServiceCaller.class })
+ static class MultipleClasses {
+
+ }
+
+ @MockBean(name = "name", classes = { ExampleService.class,
+ ExampleServiceCaller.class })
+ static class MultipleClassesWithName {
+
+ }
+
+ static class InferClassToMock {
+
+ @MockBean
+ private ExampleService exampleService;
+
+ }
+
+ @MockBean
+ static class MissingClassToMock {
+
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockResetTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockResetTests.java
new file mode 100644
index 00000000000..c9b76c389bc
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockResetTests.java
@@ -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);
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizerFactoryTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizerFactoryTests.java
new file mode 100644
index 00000000000..812eb45eb97
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizerFactoryTests.java
@@ -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 {
+
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizerTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizerTests.java
new file mode 100644
index 00000000000..4c2b0a4b715
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoContextCustomizerTests.java
@@ -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 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(Arrays.asList(d1, d2)));
+ MockitoContextCustomizer c3 = new MockitoContextCustomizer(
+ new LinkedHashSet(Arrays.asList(d2, d1)));
+ assertThat(c2.hashCode()).isEqualTo(c3.hashCode());
+ assertThat(c1).isEqualTo(c1).isNotEqualTo(c2);
+ assertThat(c2).isEqualTo(c2).isEqualTo(c3).isNotEqualTo(c1);
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoInitializeTestExecutionListenerTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoInitializeTestExecutionListenerTests.java
new file mode 100644
index 00000000000..27005a67014
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoInitializeTestExecutionListenerTests.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.test.mock.mockito;
+
+import 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 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 captor;
+
+ }
+
+ static class WithMockBeans {
+
+ @MockBean
+ InputStream mockBean;
+
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessorTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessorTests.java
new file mode 100644
index 00000000000..a6fec97388a
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessorTests.java
@@ -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();
+ }
+
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationClassForExistingBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationClassForExistingBeanIntegrationTests.java
new file mode 100644
index 00000000000..d2ded98f30f
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationClassForExistingBeanIntegrationTests.java
@@ -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 {
+
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationClassForNewBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationClassForNewBeanIntegrationTests.java
new file mode 100644
index 00000000000..8f868a0ca42
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationClassForNewBeanIntegrationTests.java
@@ -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 {
+
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationFieldForExistingBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationFieldForExistingBeanIntegrationTests.java
new file mode 100644
index 00000000000..ec5c3bf2138
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationFieldForExistingBeanIntegrationTests.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.test.mock.mockito;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.mock.mockito.example.ExampleService;
+import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller;
+import org.springframework.boot.test.mock.mockito.example.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;
+
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationFieldForNewBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationFieldForNewBeanIntegrationTests.java
new file mode 100644
index 00000000000..449b4d54c3d
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnConfigurationFieldForNewBeanIntegrationTests.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.test.mock.mockito;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.mock.mockito.example.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;
+
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnContextHierarchyIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnContextHierarchyIntegrationTests.java
new file mode 100644
index 00000000000..de0318c251e
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnContextHierarchyIntegrationTests.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.test.mock.mockito;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.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;
+ }
+
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestClassForExistingBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestClassForExistingBeanIntegrationTests.java
new file mode 100644
index 00000000000..f26bcf54063
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestClassForExistingBeanIntegrationTests.java
@@ -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 {
+
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestClassForNewBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestClassForNewBeanIntegrationTests.java
new file mode 100644
index 00000000000..67c0f73e6a0
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestClassForNewBeanIntegrationTests.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.test.mock.mockito;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.mock.mockito.example.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 {
+
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForExistingBeanCacheIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForExistingBeanCacheIntegrationTests.java
new file mode 100644
index 00000000000..95d21e4392f
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForExistingBeanCacheIntegrationTests.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.test.mock.mockito;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.mock.mockito.example.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");
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForExistingBeanConfig.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForExistingBeanConfig.java
new file mode 100644
index 00000000000..efd8414d26b
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForExistingBeanConfig.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.test.mock.mockito;
+
+import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+/**
+ * Config for {@link OnTestFieldForExistingBeanIntegrationTests} and
+ * {@link OnTestFieldForExistingBeanCacheIntegrationTests}. Extracted to a shared config
+ * to trigger caching.
+ *
+ * @author Phillip Webb
+ */
+@Configuration
+@Import(ExampleServiceCaller.class)
+public class OnTestFieldForExistingBeanConfig {
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForExistingBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForExistingBeanIntegrationTests.java
new file mode 100644
index 00000000000..109697defde
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForExistingBeanIntegrationTests.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.test.mock.mockito;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.mock.mockito.example.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");
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForNewBeanIntegrationTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForNewBeanIntegrationTests.java
new file mode 100644
index 00000000000..b074ed6f14e
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/OnTestFieldForNewBeanIntegrationTests.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.test.mock.mockito;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.mock.mockito.example.ExampleService;
+import org.springframework.boot.test.mock.mockito.example.ExampleServiceCaller;
+import org.springframework.boot.test.mock.mockito.example.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 {
+
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/ResetMocksTestExecutionListenerTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/ResetMocksTestExecutionListenerTests.java
new file mode 100644
index 00000000000..781b61d4a42
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/ResetMocksTestExecutionListenerTests.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.test.mock.mockito;
+
+import org.junit.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);
+ }
+
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/ExampleExtraInterface.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/ExampleExtraInterface.java
new file mode 100644
index 00000000000..72f57a2e6c6
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/ExampleExtraInterface.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.test.mock.mockito.example;
+
+/**
+ * Example extra interface for mocking tests.
+ *
+ * @author Phillip Webb
+ */
+public interface ExampleExtraInterface {
+
+ String doExtra();
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/ExampleService.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/ExampleService.java
new file mode 100644
index 00000000000..7915b8b8142
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/ExampleService.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.test.mock.mockito.example;
+
+/**
+ * Example service interface for mocking tests.
+ *
+ * @author Phillip Webb
+ */
+public interface ExampleService {
+
+ String greeting();
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/ExampleServiceCaller.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/ExampleServiceCaller.java
new file mode 100644
index 00000000000..176fa3f5a82
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/ExampleServiceCaller.java
@@ -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();
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/FailingExampleService.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/FailingExampleService.java
new file mode 100644
index 00000000000..8338f02345d
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/FailingExampleService.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.test.mock.mockito.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");
+ }
+
+}
diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/MyMockBean.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/MyMockBean.java
new file mode 100644
index 00000000000..d9d63692f05
--- /dev/null
+++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/example/MyMockBean.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2012-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.test.mock.mockito.example;
+
+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();
+
+}