diff --git a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-mockitobean.adoc b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-mockitobean.adoc index 818b8cc2309..6b6de7655db 100644 --- a/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-mockitobean.adoc +++ b/framework-docs/modules/ROOT/pages/testing/annotations/integration-spring/annotation-mockitobean.adoc @@ -77,7 +77,7 @@ Java:: [source,java,indent=0,subs="verbatim,quotes"] ---- class OverrideBeanTests { - @MockitoBean(name = "service") // <1> + @MockitoBean("service") // <1> private CustomService customService; // test case body... @@ -121,7 +121,7 @@ Java:: [source,java,indent=0,subs="verbatim,quotes"] ---- class OverrideBeanTests { - @MockitoSpyBean(name = "service") // <1> + @MockitoSpyBean("service") // <1> private CustomService customService; // test case body... diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBean.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBean.java index aad8207fc3f..aa221ac4ac1 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBean.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoBean.java @@ -25,6 +25,7 @@ import org.mockito.Answers; import org.mockito.MockSettings; +import org.springframework.core.annotation.AliasFor; import org.springframework.test.context.bean.override.BeanOverride; /** @@ -33,12 +34,12 @@ * {@link org.springframework.context.ApplicationContext ApplicationContext} * using a Mockito mock. * - *

By default, the bean to override is inferred from the type of the annotated + *

By default, the bean to mock is inferred from the type of the annotated * field. If multiple candidates exist, a {@code @Qualifier} annotation can be * used to help disambiguate. In the absence of a {@code @Qualifier} annotation, * the name of the annotated field will be used as a fallback qualifier. - * Alternatively, you can explicitly specify a bean name to replace by setting the - * {@link #name() name} attribute. + * Alternatively, you can explicitly specify a bean name to mock by setting the + * {@link #value() value} or {@link #name() name} attribute. * *

A new bean definition will be created if a corresponding bean definition does * not exist. However, if you would like for the test to fail when a corresponding @@ -52,7 +53,7 @@ * the context alongside the existing dependency. * *

NOTE: Only singleton beans can be overridden. - * Any attempt to override a non-singleton bean will result in an exception. + * Any attempt to mock a non-singleton bean will result in an exception. * * @author Simon Baslé * @author Sam Brannen @@ -67,12 +68,22 @@ public @interface MockitoBean { /** - * The name of the bean to register or replace. - *

If left unspecified, the bean to override is selected according to - * the annotated field's type, taking qualifiers into account if necessary. - * See the {@linkplain MockitoBean class-level documentation} for details. - * @return the name of the mocked bean + * Alias for {@link #name()}. + *

Intended to be used when no other attributes are needed — for + * example, {@code @MockitoBean("customBeanName")}. + * @see #name() */ + @AliasFor("name") + String value() default ""; + + /** + * Name of the bean to mock. + *

If left unspecified, the bean to mock is selected according to the + * annotated field's type, taking qualifiers into account if necessary. See + * the {@linkplain MockitoBean class-level documentation} for details. + * @see #value() + */ + @AliasFor("value") String name() default ""; /** diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBean.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBean.java index 4d1f434df6b..ac6225f3a21 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBean.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBean.java @@ -24,28 +24,32 @@ import org.mockito.Mockito; +import org.springframework.core.annotation.AliasFor; import org.springframework.test.context.bean.override.BeanOverride; /** * Mark a field to trigger a bean override using a Mockito spy, which will wrap - * the original instance. + * the original bean instance. * - *

If no explicit {@link #name()} is specified, a target bean is selected - * according to the class of the annotated field, and there must be exactly one - * such candidate bean. A {@code @Qualifier} annotation can be used to help - * disambiguate. - * If a {@link #name()} is specified, it is required that a target bean of that - * name has been previously registered in the application context. + *

By default, the bean to spy is inferred from the type of the annotated + * field. If multiple candidates exist, a {@code @Qualifier} annotation can be + * used to help disambiguate. In the absence of a {@code @Qualifier} annotation, + * the name of the annotated field will be used as a fallback qualifier. + * Alternatively, you can explicitly specify a bean name to spy by setting the + * {@link #value() value} or {@link #name() name} attribute. If a bean name is + * specified, it is required that a target bean with that name has been previously + * registered in the application context. * - *

Dependencies that are known to the application context but are not beans - * (such as those + *

A spy cannot be created for components which are known to the application + * context but are not beans — for example, components * {@link org.springframework.beans.factory.config.ConfigurableListableBeanFactory#registerResolvableDependency(Class, Object) - * registered directly}) will not be found. + * registered directly} as resolvable dependencies. * - *

NOTE: Only singleton beans can be overridden. - * Any attempt to override a non-singleton bean will result in an exception. + *

NOTE: Only singleton beans can be spied. + * Any attempt to create a spy for a non-singleton bean will result in an exception. * * @author Simon Baslé + * @author Sam Brannen * @since 6.2 * @see org.springframework.test.context.bean.override.mockito.MockitoBean @MockitoBean * @see org.springframework.test.context.bean.override.convention.TestBean @TestBean @@ -57,12 +61,22 @@ public @interface MockitoSpyBean { /** - * The name of the bean to spy. - *

If left unspecified, the bean to spy is selected according to - * the annotated field's type, taking qualifiers into account if necessary. - * See the {@linkplain MockitoSpyBean class-level documentation} for details. - * @return the name of the spied bean + * Alias for {@link #name()}. + *

Intended to be used when no other attributes are needed — for + * example, {@code @MockitoSpyBean("customBeanName")}. + * @see #name() */ + @AliasFor("name") + String value() default ""; + + /** + * Name of the bean to spy. + *

If left unspecified, the bean to spy is selected according to the + * annotated field's type, taking qualifiers into account if necessary. See + * the {@linkplain MockitoSpyBean class-level documentation} for details. + * @see #value() + */ + @AliasFor("value") String name() default ""; /** diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanContextCustomizerEqualityTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanContextCustomizerEqualityTests.java index a801171ca30..5435a45a121 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanContextCustomizerEqualityTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanContextCustomizerEqualityTests.java @@ -17,12 +17,12 @@ package org.springframework.test.context.bean.override.mockito; import org.junit.jupiter.api.Test; -import org.mockito.Answers; import org.springframework.test.context.ContextCustomizer; import org.springframework.test.context.bean.override.BeanOverrideContextCustomizerTestUtils; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Answers.RETURNS_MOCKS; /** * Tests that validate the behavior of {@link MockitoBean} and @@ -85,7 +85,7 @@ private ContextCustomizer createContextCustomizer(Class testClass) { static class Case1ByName { - @MockitoBean(name = "serviceBean") + @MockitoBean("serviceBean") private String exampleService; } @@ -99,7 +99,7 @@ static class Case1ByType { static class Case2ByName { - @MockitoBean(name = "serviceBean") + @MockitoBean("serviceBean") private String serviceToMock; } @@ -120,14 +120,14 @@ static class Case2ByTypeSameFieldName { static class Case3 { - @MockitoBean(answers = Answers.RETURNS_MOCKS) + @MockitoBean(answers = RETURNS_MOCKS) private String exampleService; } static class Case4ByName { - @MockitoSpyBean(name = "serviceBean") + @MockitoSpyBean("serviceBean") private String exampleService; } @@ -141,7 +141,7 @@ static class Case4ByType { static class Case5ByName { - @MockitoSpyBean(name = "serviceBean") + @MockitoSpyBean("serviceBean") private String serviceToMock; } diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanForByNameLookupIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanForByNameLookupIntegrationTests.java index d2d22530962..321866d464b 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanForByNameLookupIntegrationTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanForByNameLookupIntegrationTests.java @@ -36,22 +36,22 @@ @SpringJUnitConfig public class MockitoBeanForByNameLookupIntegrationTests { - @MockitoBean(name = "field") + @MockitoBean("field") ExampleService field; - @MockitoBean(name = "nestedField") + @MockitoBean("nestedField") ExampleService nestedField; - @MockitoBean(name = "field") + @MockitoBean("field") ExampleService renamed1; - @MockitoBean(name = "nestedField") + @MockitoBean("nestedField") ExampleService renamed2; - @MockitoBean(name = "nonExistingBean") + @MockitoBean("nonExistingBean") ExampleService nonExisting1; - @MockitoBean(name = "nestedNonExistingBean") + @MockitoBean("nestedNonExistingBean") ExampleService nonExisting2; diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideMetadataTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideMetadataTests.java index 99aad2960db..0a1a222b5bc 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideMetadataTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanOverrideMetadataTests.java @@ -24,6 +24,7 @@ import org.mockito.Answers; import org.springframework.core.ResolvableType; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.test.context.bean.override.OverrideMetadata; import org.springframework.util.ReflectionUtils; @@ -114,7 +115,7 @@ private Field sampleField(String fieldName) { } private MockitoBeanOverrideMetadata createMetadata(Field field) { - MockitoBean annotation = field.getAnnotation(MockitoBean.class); + MockitoBean annotation = AnnotatedElementUtils.getMergedAnnotation(field, MockitoBean.class); return new MockitoBeanOverrideMetadata(field, ResolvableType.forClass(field.getType()), annotation); } @@ -128,7 +129,7 @@ static class SampleOneMock { static class SampleOneMockWithName { - @MockitoBean(name = "anotherService") + @MockitoBean("anotherService") String service; } @@ -144,7 +145,7 @@ static class Sample { @MockitoBean(name = "beanToMock") private String service3; - @MockitoBean(name = "beanToMock") + @MockitoBean(value = "beanToMock") private String service4; @MockitoBean(extraInterfaces = Externalizable.class) diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanSettingsStrictIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanSettingsStrictIntegrationTests.java index a54fbf3756b..2f998b53598 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanSettingsStrictIntegrationTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanSettingsStrictIntegrationTests.java @@ -26,7 +26,6 @@ import org.junit.platform.testkit.engine.EngineTestKit; import org.junit.platform.testkit.engine.Events; import org.mockito.exceptions.misusing.UnnecessaryStubbingException; -import org.mockito.quality.Strictness; import org.springframework.context.annotation.Configuration; import org.springframework.test.annotation.DirtiesContext; @@ -42,6 +41,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.BDDMockito.when; import static org.mockito.Mockito.mock; +import static org.mockito.quality.Strictness.STRICT_STUBS; /** * Integration tests ensuring unnecessary stubbing is reported in various @@ -85,7 +85,7 @@ void unnecessaryStub() { @SpringJUnitConfig(Config.class) @DirtiesContext - @MockitoBeanSettings(Strictness.STRICT_STUBS) + @MockitoBeanSettings(STRICT_STUBS) static class ExplicitStrictness extends BaseCase { } diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanWithResetIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanWithResetIntegrationTests.java index 7867d154e9b..d75e512170e 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanWithResetIntegrationTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoBeanWithResetIntegrationTests.java @@ -33,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; +import static org.springframework.test.context.bean.override.mockito.MockReset.BEFORE; /** * Integration tests for {@link MockitoBean} that validate automatic reset @@ -45,10 +46,10 @@ @TestMethodOrder(OrderAnnotation.class) public class MockitoBeanWithResetIntegrationTests { - @MockitoBean(reset = MockReset.BEFORE) + @MockitoBean(reset = BEFORE) ExampleService service; - @MockitoBean(reset = MockReset.BEFORE) + @MockitoBean(reset = BEFORE) FailingExampleService failingService; @Order(1) diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanForByNameLookupIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanForByNameLookupIntegrationTests.java index 14f1d1f4c59..1310a18d436 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanForByNameLookupIntegrationTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanForByNameLookupIntegrationTests.java @@ -40,16 +40,16 @@ @SpringJUnitConfig(Config.class) public class MockitoSpyBeanForByNameLookupIntegrationTests { - @MockitoSpyBean(name = "field") + @MockitoSpyBean("field") ExampleService field; - @MockitoSpyBean(name = "nestedField") + @MockitoSpyBean("nestedField") ExampleService nestedField; - @MockitoSpyBean(name = "field") + @MockitoSpyBean("field") ExampleService renamed1; - @MockitoSpyBean(name = "nestedField") + @MockitoSpyBean("nestedField") ExampleService renamed2; diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideMetadataTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideMetadataTests.java index 1872aede339..4c91f431022 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideMetadataTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanOverrideMetadataTests.java @@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test; import org.springframework.core.ResolvableType; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.test.context.bean.override.OverrideMetadata; import org.springframework.util.ReflectionUtils; @@ -105,7 +106,7 @@ private Field sampleField(String fieldName) { } private MockitoSpyBeanOverrideMetadata createMetadata(Field field) { - MockitoSpyBean annotation = field.getAnnotation(MockitoSpyBean.class); + MockitoSpyBean annotation = AnnotatedElementUtils.getMergedAnnotation(field, MockitoSpyBean.class); return new MockitoSpyBeanOverrideMetadata(field, ResolvableType.forClass(field.getType()), annotation); } @@ -119,7 +120,7 @@ static class SampleOneSpy { static class SampleOneSpyWithName { - @MockitoSpyBean(name = "anotherService") + @MockitoSpyBean("anotherService") String service; } @@ -132,10 +133,10 @@ static class Sample { @MockitoSpyBean private String service2; - @MockitoSpyBean(name = "beanToMock") + @MockitoSpyBean(name = "beanToSpy") private String service3; - @MockitoSpyBean(name = "beanToMock") + @MockitoSpyBean(value = "beanToSpy") private String service4; @MockitoSpyBean(reset = MockReset.BEFORE) diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanTests.java index 86cdb8ca474..fce7ab3f73e 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanTests.java @@ -81,7 +81,7 @@ static class ByTypeSingleLookup { static class ByNameSingleLookup { - @MockitoSpyBean(name = "beanToSpy") + @MockitoSpyBean("beanToSpy") String example; } diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanWithResetIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanWithResetIntegrationTests.java index ced11e1b2d8..cecde1de0dc 100644 --- a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanWithResetIntegrationTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/MockitoSpyBeanWithResetIntegrationTests.java @@ -22,11 +22,9 @@ import org.junit.jupiter.api.TestMethodOrder; import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.lang.Nullable; import org.springframework.test.context.bean.override.example.ExampleService; import org.springframework.test.context.bean.override.example.FailingExampleService; import org.springframework.test.context.bean.override.example.RealExampleService; @@ -35,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.mockito.Mockito.doReturn; +import static org.springframework.test.context.bean.override.mockito.MockReset.BEFORE; /** * Integration tests for {@link MockitoSpyBean} that validate automatic reset @@ -47,12 +46,13 @@ @TestMethodOrder(OrderAnnotation.class) public class MockitoSpyBeanWithResetIntegrationTests { - @MockitoSpyBean(reset = MockReset.BEFORE) + @MockitoSpyBean(reset = BEFORE) ExampleService service; - @MockitoSpyBean(reset = MockReset.BEFORE) + @MockitoSpyBean(name = "failingExampleServiceFactory", reset = BEFORE) FailingExampleService failingService; + @Order(1) @Test void beanFirstEstablishingStub(ApplicationContext ctx) { @@ -83,21 +83,23 @@ void factoryBeanFirstEstablishingStub(ApplicationContext ctx) { @Order(4) @Test void factoryBeanSecondEnsuringStubReset(ApplicationContext ctx) { - assertThat(ctx.getBean("factory")).isNotNull().isSameAs(this.failingService); + assertThat(ctx.getBean("failingExampleServiceFactory")) + .isNotNull() + .isSameAs(this.failingService); assertThatIllegalStateException().isThrownBy(this.failingService::greeting) .as("not stubbed") .withMessage("Failed"); } + static class FailingExampleServiceFactory implements FactoryBean { - @Nullable + @Override public FailingExampleService getObject() { return new FailingExampleService(); } - @Nullable @Override public Class getObjectType() { return FailingExampleService.class; @@ -107,14 +109,13 @@ public Class getObjectType() { @Configuration(proxyBeanMethods = false) static class Config { - @Bean("service") - ExampleService bean1() { + @Bean + ExampleService service() { return new RealExampleService("Production hello"); } - @Bean("factory") - @Qualifier("factory") - FailingExampleServiceFactory factory() { + @Bean + FailingExampleServiceFactory failingExampleServiceFactory() { return new FailingExampleServiceFactory(); } }