From e2d981e80950c51c16e3ac7a59220ec5d8e383b5 Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Fri, 11 Oct 2024 10:13:35 +0200 Subject: [PATCH] Extract common logic into AbstractMockitoTestExecutionListener --- ...AbstractMockitoTestExecutionListener.java} | 36 ++++++++++++++----- .../MockitoResetTestExecutionListener.java | 15 ++++---- .../mockito/MockitoTestExecutionListener.java | 27 ++++++-------- 3 files changed, 44 insertions(+), 34 deletions(-) rename spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/{MockitoAnnotationDetector.java => AbstractMockitoTestExecutionListener.java} (61%) diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoAnnotationDetector.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/AbstractMockitoTestExecutionListener.java similarity index 61% rename from spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoAnnotationDetector.java rename to spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/AbstractMockitoTestExecutionListener.java index ed102c702d..b368af1032 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoAnnotationDetector.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/AbstractMockitoTestExecutionListener.java @@ -22,28 +22,45 @@ import java.util.Arrays; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; +import org.springframework.test.context.TestContext; +import org.springframework.test.context.support.AbstractTestExecutionListener; +import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; /** - * Utility class that detects {@code org.mockito} annotations as well as the - * annotations in this package (like {@link MockitoBeanSettings @MockitoBeanSettings}). + * Abstract base class for {@code TestExecutionListener} implementations involving + * Mockito. * - * @author Simon Baslé * @author Sam Brannen + * @author Simon Baslé + * @since 6.2 */ -abstract class MockitoAnnotationDetector { +abstract class AbstractMockitoTestExecutionListener extends AbstractTestExecutionListener { - private static final String MOCKITO_BEAN_PACKAGE = MockitoBeanSettings.class.getPackageName(); + static final boolean mockitoPresent = ClassUtils.isPresent("org.mockito.Mockito", + AbstractMockitoTestExecutionListener.class.getClassLoader()); + + private static final String SPRING_MOCKITO_PACKAGE = "org.springframework.test.context.bean.override.mockito"; private static final String ORG_MOCKITO_PACKAGE = "org.mockito"; private static final Predicate isMockitoAnnotation = annotation -> { String packageName = annotation.annotationType().getPackageName(); - return (packageName.startsWith(MOCKITO_BEAN_PACKAGE) || + return (packageName.startsWith(SPRING_MOCKITO_PACKAGE) || packageName.startsWith(ORG_MOCKITO_PACKAGE)); }; - static boolean hasMockitoAnnotations(Class testClass) { + + /** + * Determine if the test class for the supplied {@linkplain TestContext + * test context} uses {@code org.mockito} annotations or any of the annotations + * in this package (such as {@link MockitoBeanSettings @MockitoBeanSettings}). + */ + static boolean hasMockitoAnnotations(TestContext testContext) { + return hasMockitoAnnotations(testContext.getTestClass()); + } + + private static boolean hasMockitoAnnotations(Class testClass) { if (isAnnotated(testClass)) { return true; } @@ -53,9 +70,10 @@ abstract class MockitoAnnotationDetector { // previous invocation of isAnnotated(testClass) only finds annotations declared directly // on the test class. So, we'll likely need a completely different approach that combines // the "test class/interface is annotated?" and "field is annotated?" checks in a single - // search algorithm. + // search algorithm, and we'll also need to support @Nested class hierarchies. AtomicBoolean found = new AtomicBoolean(); - ReflectionUtils.doWithFields(testClass, field -> found.set(true), MockitoAnnotationDetector::isAnnotated); + ReflectionUtils.doWithFields(testClass, + field -> found.set(true), AbstractMockitoTestExecutionListener::isAnnotated); return found.get(); } diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoResetTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoResetTestExecutionListener.java index 228acf995d..63f0d6329f 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoResetTestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoResetTestExecutionListener.java @@ -32,7 +32,6 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.Ordered; import org.springframework.lang.Nullable; import org.springframework.test.context.TestContext; -import org.springframework.test.context.support.AbstractTestExecutionListener; /** * {@code TestExecutionListener} that resets any mock beans that have been marked @@ -45,7 +44,7 @@ import org.springframework.test.context.support.AbstractTestExecutionListener; * @see MockitoBean @MockitoBean * @see MockitoSpyBean @MockitoSpyBean */ -public class MockitoResetTestExecutionListener extends AbstractTestExecutionListener { +public class MockitoResetTestExecutionListener extends AbstractMockitoTestExecutionListener { /** * Executes before {@link org.springframework.test.context.bean.override.BeanOverrideTestExecutionListener}. @@ -56,17 +55,15 @@ public class MockitoResetTestExecutionListener extends AbstractTestExecutionList } @Override - public void beforeTestMethod(TestContext testContext) throws Exception { - Class testClass = testContext.getTestClass(); - if (MockitoTestExecutionListener.mockitoPresent && MockitoAnnotationDetector.hasMockitoAnnotations(testClass)) { + public void beforeTestMethod(TestContext testContext) { + if (mockitoPresent && hasMockitoAnnotations(testContext)) { resetMocks(testContext.getApplicationContext(), MockReset.BEFORE); } } @Override - public void afterTestMethod(TestContext testContext) throws Exception { - Class testClass = testContext.getTestClass(); - if (MockitoTestExecutionListener.mockitoPresent && MockitoAnnotationDetector.hasMockitoAnnotations(testClass)) { + public void afterTestMethod(TestContext testContext) { + if (mockitoPresent && hasMockitoAnnotations(testContext)) { resetMocks(testContext.getApplicationContext(), MockReset.AFTER); } } @@ -114,7 +111,7 @@ public class MockitoResetTestExecutionListener extends AbstractTestExecutionList return beanFactory.getSingleton(beanName); } - private static boolean isStandardBeanOrSingletonFactoryBean(ConfigurableListableBeanFactory beanFactory, String beanName) { + private static boolean isStandardBeanOrSingletonFactoryBean(BeanFactory beanFactory, String beanName) { String factoryBeanName = BeanFactory.FACTORY_BEAN_PREFIX + beanName; if (beanFactory.containsBean(factoryBeanName)) { FactoryBean factoryBean = (FactoryBean) beanFactory.getBean(factoryBeanName); diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoTestExecutionListener.java index e8cf7799bf..01eb7afc49 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoTestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoTestExecutionListener.java @@ -22,9 +22,7 @@ import org.mockito.quality.Strictness; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.test.context.TestContext; -import org.springframework.test.context.support.AbstractTestExecutionListener; import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; -import org.springframework.util.ClassUtils; /** * {@code TestExecutionListener} that enables {@link MockitoBean @MockitoBean} @@ -49,12 +47,10 @@ import org.springframework.util.ClassUtils; * @see MockitoBean @MockitoBean * @see MockitoSpyBean @MockitoSpyBean */ -public class MockitoTestExecutionListener extends AbstractTestExecutionListener { +public class MockitoTestExecutionListener extends AbstractMockitoTestExecutionListener { - private static final String MOCKITO_SESSION_ATTRIBUTE_NAME = MockitoTestExecutionListener.class.getName() + ".mockitoSession"; - - static final boolean mockitoPresent = ClassUtils.isPresent("org.mockito.Mockito", - MockitoTestExecutionListener.class.getClassLoader()); + private static final String MOCKITO_SESSION_ATTRIBUTE_NAME = + MockitoTestExecutionListener.class.getName() + ".mockitoSession"; /** @@ -66,7 +62,7 @@ public class MockitoTestExecutionListener extends AbstractTestExecutionListener } @Override - public void prepareTestInstance(TestContext testContext) throws Exception { + public void prepareTestInstance(TestContext testContext) { if (mockitoPresent) { closeMocks(testContext); initMocks(testContext); @@ -74,7 +70,7 @@ public class MockitoTestExecutionListener extends AbstractTestExecutionListener } @Override - public void beforeTestMethod(TestContext testContext) throws Exception { + public void beforeTestMethod(TestContext testContext) { if (mockitoPresent && Boolean.TRUE.equals( testContext.getAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE))) { closeMocks(testContext); @@ -83,22 +79,22 @@ public class MockitoTestExecutionListener extends AbstractTestExecutionListener } @Override - public void afterTestMethod(TestContext testContext) throws Exception { + public void afterTestMethod(TestContext testContext) { if (mockitoPresent) { closeMocks(testContext); } } @Override - public void afterTestClass(TestContext testContext) throws Exception { + public void afterTestClass(TestContext testContext) { if (mockitoPresent) { closeMocks(testContext); } } private static void initMocks(TestContext testContext) { - Class testClass = testContext.getTestClass(); - if (MockitoAnnotationDetector.hasMockitoAnnotations(testClass)) { + if (hasMockitoAnnotations(testContext)) { + Class testClass = testContext.getTestClass(); Object testInstance = testContext.getTestInstance(); MockitoBeanSettings annotation = AnnotationUtils.findAnnotation(testClass, MockitoBeanSettings.class); Strictness strictness = (annotation != null ? annotation.value() : Strictness.STRICT_STUBS); @@ -113,9 +109,8 @@ public class MockitoTestExecutionListener extends AbstractTestExecutionListener .startMocking(); } - private static void closeMocks(TestContext testContext) throws Exception { - Object mocks = testContext.getAttribute(MOCKITO_SESSION_ATTRIBUTE_NAME); - if (mocks instanceof MockitoSession session) { + private static void closeMocks(TestContext testContext) { + if (testContext.getAttribute(MOCKITO_SESSION_ATTRIBUTE_NAME) instanceof MockitoSession session) { session.finishMocking(); } }