Adapt to Mockito support in the Test Context Framework

This commit updates MockitoTestExecutionListener to not handle mocks
as this is already done in the listener provided by the core framework,
and registration can only happen once.

Integration tests have been left as-is to validate that the presence
of both listeners doesn't have an unwanted side effect.

See gh-41177
This commit is contained in:
Stéphane Nicoll 2024-03-12 08:26:23 +01:00 committed by Andy Wilkinson
parent 2053e13c09
commit 305bfb1641
2 changed files with 2 additions and 76 deletions

View File

@ -16,27 +16,18 @@
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.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. Also triggers
* {@link MockitoAnnotations#openMocks(Object)} when any Mockito annotations used,
* primarily to allow {@link Captor @Captor} annotations.
* {@link SpyBean @SpyBean} support.
* <p>
* To use the automatic reset support of {@code @MockBean} and {@code @SpyBean}, configure
* {@link ResetMocksTestExecutionListener} as well.
@ -49,8 +40,6 @@ import org.springframework.util.ReflectionUtils.FieldCallback;
*/
public class MockitoTestExecutionListener extends AbstractTestExecutionListener {
private static final String MOCKS_ATTRIBUTE_NAME = MockitoTestExecutionListener.class.getName() + ".mocks";
@Override
public final int getOrder() {
return 1950;
@ -58,8 +47,6 @@ public class MockitoTestExecutionListener extends AbstractTestExecutionListener
@Override
public void prepareTestInstance(TestContext testContext) throws Exception {
closeMocks(testContext);
initMocks(testContext);
injectFields(testContext);
}
@ -67,41 +54,10 @@ 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));
@ -130,28 +86,6 @@ public class MockitoTestExecutionListener extends AbstractTestExecutionListener
}
}
/**
* {@link FieldCallback} to collect Mockito annotations.
*/
private static final class MockitoAnnotationCollection implements FieldCallback {
private final Set<Annotation> annotations = new LinkedHashSet<>();
@Override
public void doWith(Field field) throws IllegalArgumentException {
for (Annotation annotation : field.getDeclaredAnnotations()) {
if (annotation.annotationType().getName().startsWith("org.mockito")) {
this.annotations.add(annotation);
}
}
}
boolean hasAnnotations() {
return !this.annotations.isEmpty();
}
}
private static final class MockitoField {
private final Field field;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2024 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.
@ -53,14 +53,6 @@ class MockitoTestExecutionListenerTests {
@Mock
private MockitoPostProcessor postProcessor;
@Test
void prepareTestInstanceShouldInitMockitoAnnotations() throws Exception {
WithMockitoAnnotations instance = new WithMockitoAnnotations();
this.listener.prepareTestInstance(mockTestContext(instance));
assertThat(instance.mock).isNotNull();
assertThat(instance.captor).isNotNull();
}
@Test
void prepareTestInstanceShouldInjectMockBean() throws Exception {
given(this.applicationContext.getBean(MockitoPostProcessor.class)).willReturn(this.postProcessor);