diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListener.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListener.java index 56d9568457..941b878403 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListener.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoTestExecutionListener.java @@ -16,18 +16,29 @@ 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 java.util.function.BiConsumer; +import org.mockito.Captor; +import org.mockito.MockitoAnnotations; + import org.springframework.test.context.TestContext; import org.springframework.test.context.TestExecutionListener; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import org.springframework.test.context.support.AbstractTestExecutionListener; import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; import org.springframework.util.ReflectionUtils; +import org.springframework.util.ReflectionUtils.FieldCallback; /** * {@link TestExecutionListener} to enable {@link MockBean @MockBean} and - * {@link SpyBean @SpyBean} support. + * {@link SpyBean @SpyBean} support. Also triggers + * {@link MockitoAnnotations#openMocks(Object)} when any Mockito annotations used, + * primarily to allow {@link Captor @Captor} annotations. *
* To use the automatic reset support of {@code @MockBean} and {@code @SpyBean}, configure
* {@link ResetMocksTestExecutionListener} as well.
@@ -37,13 +48,15 @@ import org.springframework.util.ReflectionUtils;
* @author Moritz Halbritter
* @since 1.4.2
* @see ResetMocksTestExecutionListener
- * @deprecated since 3.4.0 for removal in 3.6.0 in favor of
- * {@link org.springframework.test.context.bean.override.mockito.MockitoTestExecutionListener}
+ * @deprecated since 3.4.0 for removal in 3.6.0 in favor of Spring Framework's support for
+ * {@link MockitoBean} and {@link MockitoSpyBean}.
*/
@SuppressWarnings("removal")
@Deprecated(since = "3.4.0", forRemoval = true)
public class MockitoTestExecutionListener extends AbstractTestExecutionListener {
+ private static final String MOCKS_ATTRIBUTE_NAME = MockitoTestExecutionListener.class.getName() + ".mocks";
+
@Override
public final int getOrder() {
return 1950;
@@ -51,6 +64,8 @@ public class MockitoTestExecutionListener extends AbstractTestExecutionListener
@Override
public void prepareTestInstance(TestContext testContext) throws Exception {
+ closeMocks(testContext);
+ initMocks(testContext);
injectFields(testContext);
}
@@ -58,10 +73,41 @@ public class MockitoTestExecutionListener extends AbstractTestExecutionListener
public void beforeTestMethod(TestContext testContext) throws Exception {
if (Boolean.TRUE.equals(
testContext.getAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE))) {
+ closeMocks(testContext);
+ initMocks(testContext);
reinjectFields(testContext);
}
}
+ @Override
+ public void afterTestMethod(TestContext testContext) throws Exception {
+ closeMocks(testContext);
+ }
+
+ @Override
+ public void afterTestClass(TestContext testContext) throws Exception {
+ closeMocks(testContext);
+ }
+
+ private void initMocks(TestContext testContext) {
+ if (hasMockitoAnnotations(testContext)) {
+ testContext.setAttribute(MOCKS_ATTRIBUTE_NAME, MockitoAnnotations.openMocks(testContext.getTestInstance()));
+ }
+ }
+
+ private void closeMocks(TestContext testContext) throws Exception {
+ Object mocks = testContext.getAttribute(MOCKS_ATTRIBUTE_NAME);
+ if (mocks instanceof AutoCloseable closeable) {
+ closeable.close();
+ }
+ }
+
+ private boolean hasMockitoAnnotations(TestContext testContext) {
+ MockitoAnnotationCollection collector = new MockitoAnnotationCollection();
+ ReflectionUtils.doWithFields(testContext.getTestClass(), collector);
+ return collector.hasAnnotations();
+ }
+
private void injectFields(TestContext testContext) {
postProcessFields(testContext, (mockitoField, postProcessor) -> postProcessor.inject(mockitoField.field,
mockitoField.target, mockitoField.definition));
@@ -90,6 +136,28 @@ public class MockitoTestExecutionListener extends AbstractTestExecutionListener
}
}
+ /**
+ * {@link FieldCallback} to collect Mockito annotations.
+ */
+ private static final class MockitoAnnotationCollection implements FieldCallback {
+
+ private final Set