Extract common logic into AbstractMockitoTestExecutionListener

This commit is contained in:
Sam Brannen
2024-10-11 10:13:35 +02:00
parent 0f25c75b9e
commit e2d981e809
3 changed files with 44 additions and 34 deletions

View File

@@ -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<Annotation> 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();
}

View File

@@ -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);

View File

@@ -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();
}
}