diff --git a/spring-boot-test/pom.xml b/spring-boot-test/pom.xml index a7c2ae83cc4..e09540488cd 100644 --- a/spring-boot-test/pom.xml +++ b/spring-boot-test/pom.xml @@ -55,6 +55,11 @@ hamcrest-library true + + org.mockito + mockito-core + true + 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 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 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 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(); + +}