Remove support for Mockito annotations and MockitoSession

This change remove the support for Mockito annotations, `MockitoSession`
and opening/closing of mocks that was inherited from Boot's `@MockBean`
support, as well as the switch to `MockitoSession` made in 1c893e6.

Attempting to take responsability for things Mockito's own JUnit
Jupiter extension does better is not ideal, and we found it leads to
several corner cases which make `SpringExtension` and `MockitoExtension`
incompatible in the current approach.

Instead, this change refocuses our Mockito bean overriding support
exclusively on aspects specific to the Framework. `MockitoExtension`
will thus be usable in conjunction with `SpringExtension` if one needs
to use `@Captor`/`@InitMocks`/`@Mock`/`@Spy` or other Mockito utilities.

See gh-33318
Closes gh-33692
This commit is contained in:
Simon Baslé
2024-10-15 18:29:36 +02:00
parent c261ca3102
commit 6f79c7e70f
14 changed files with 5 additions and 432 deletions

View File

@@ -66,8 +66,6 @@ package org.springframework.test.context;
* DirtiesContextBeforeModesTestExecutionListener}</li>
* <li>{@link org.springframework.test.context.event.ApplicationEventsTestExecutionListener
* ApplicationEventsTestExecutionListener}</li>
* <li>{@link org.springframework.test.context.bean.override.mockito.MockitoTestExecutionListener
* MockitoTestExecutionListener}</li>
* <li>{@link org.springframework.test.context.support.DependencyInjectionTestExecutionListener
* DependencyInjectionTestExecutionListener}</li>
* <li>{@link org.springframework.test.context.observation.MicrometerObservationRegistryTestExecutionListener

View File

@@ -82,7 +82,6 @@ public @interface TestExecutionListeners {
* @see org.springframework.test.context.web.ServletTestExecutionListener
* @see org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener
* @see org.springframework.test.context.event.ApplicationEventsTestExecutionListener
* @see org.springframework.test.context.bean.override.mockito.MockitoTestExecutionListener
* @see org.springframework.test.context.support.DependencyInjectionTestExecutionListener
* @see org.springframework.test.context.support.DirtiesContextTestExecutionListener
* @see org.springframework.test.context.support.CommonCachesTestExecutionListener

View File

@@ -42,19 +42,16 @@ abstract class AbstractMockitoTestExecutionListener extends AbstractTestExecutio
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<MergedAnnotation<?>> isMockitoAnnotation = mergedAnnotation -> {
String packageName = mergedAnnotation.getType().getPackageName();
return (packageName.startsWith(SPRING_MOCKITO_PACKAGE) ||
packageName.startsWith(ORG_MOCKITO_PACKAGE));
return packageName.startsWith(SPRING_MOCKITO_PACKAGE);
};
/**
* 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}).
* test context} uses any of the annotations in this package (such as
* {@link MockitoBean @MockitoBean}).
*/
static boolean hasMockitoAnnotations(TestContext testContext) {
return hasMockitoAnnotations(testContext.getTestClass());

View File

@@ -1,47 +0,0 @@
/*
* Copyright 2002-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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.mockito.quality.Strictness;
/**
* Configure a test class that uses {@link MockitoBean @MockitoBean} or
* {@link MockitoSpyBean @MockitoSpyBean} to set up Mockito with an explicit
* stubbing strictness mode.
*
* @author Simon Baslé
* @since 6.2
* @see MockitoTestExecutionListener
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MockitoBeanSettings {
/**
* The stubbing strictness mode to apply for all Mockito mocks in the annotated
* test class.
*/
Strictness value();
}

View File

@@ -40,7 +40,6 @@ import org.springframework.test.context.TestContext;
* @author Phillip Webb
* @author Sam Brannen
* @since 6.2
* @see MockitoTestExecutionListener
* @see MockitoBean @MockitoBean
* @see MockitoSpyBean @MockitoSpyBean
*/

View File

@@ -1,102 +0,0 @@
/*
* Copyright 2002-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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.context.bean.override.mockito;
import org.mockito.Mockito;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestContextAnnotationUtils;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
/**
* {@code TestExecutionListener} that manages a {@link MockitoSession} for each
* test class that uses {@link MockitoBean @MockitoBean},
* {@link MockitoSpyBean @MockitoSpyBean},
* {@link MockitoBeanSettings @MockitoBeanSettings}, or any annotations from the
* {@code org.mockito} package.
*
* <p>The {@link MockitoSession#setStrictness(Strictness) strictness} of the
* session defaults to {@link Strictness#STRICT_STUBS}. Use
* {@code @MockitoBeanSettings} to specify a different strictness.
*
* <p>Dependency injection for {@code @MockitoBean} and {@code @MockitoSpyBean}
* fields is handled by the
* {@link org.springframework.test.context.bean.override.BeanOverrideTestExecutionListener
* BeanOverrideTestExecutionListener}, and automatic reset support for
* {@code @MockitoBean} and {@code @MockitoSpyBean} is handled by the
* {@link MockitoResetTestExecutionListener}.
*
* @author Simon Baslé
* @author Sam Brannen
* @since 6.2
* @see MockitoResetTestExecutionListener
* @see MockitoBean @MockitoBean
* @see MockitoSpyBean @MockitoSpyBean
*/
public class MockitoTestExecutionListener extends AbstractMockitoTestExecutionListener {
private static final String MOCKITO_SESSION_ATTRIBUTE_NAME =
MockitoTestExecutionListener.class.getName() + ".mockitoSession";
/**
* Executes before {@link DependencyInjectionTestExecutionListener}.
*/
@Override
public final int getOrder() {
return 1950;
}
@Override
public void beforeTestMethod(TestContext testContext) {
if (mockitoPresent && hasMockitoAnnotations(testContext)) {
initMocks(testContext);
}
}
@Override
public void afterTestMethod(TestContext testContext) {
if (mockitoPresent && hasMockitoAnnotations(testContext)) {
closeMocks(testContext);
}
}
private static void initMocks(TestContext testContext) {
Class<?> testClass = testContext.getTestClass();
Object testInstance = testContext.getTestInstance();
MockitoBeanSettings annotation =
TestContextAnnotationUtils.findMergedAnnotation(testClass, MockitoBeanSettings.class);
Strictness strictness = (annotation != null ? annotation.value() : Strictness.STRICT_STUBS);
testContext.setAttribute(MOCKITO_SESSION_ATTRIBUTE_NAME, initMockitoSession(testInstance, strictness));
}
private static MockitoSession initMockitoSession(Object testInstance, Strictness strictness) {
return Mockito.mockitoSession()
.initMocks(testInstance)
.strictness(strictness)
.startMocking();
}
private static void closeMocks(TestContext testContext) {
if (testContext.getAttribute(MOCKITO_SESSION_ATTRIBUTE_NAME) instanceof MockitoSession session) {
session.finishMocking();
}
}
}